تایپاسکریپت - انگولار: بازگرداندن Observable/Promise در ES6 از FileReader
من سعی داشتم نتیجه را از FileReader برگردانم و این پیادهسازی را پیدا کردم. اما از آنجایی که قدیمی است، میخواهم بدانم چگونه میتوان همان کار را با استفاده از Promises در ES6 یا Observables در Rx پیادهسازی کرد.
در زیر کد من با ارجاع به لینک مذکور آمده است و همانطور که انتظار میرفت کار میکند.
import { Injectable } from '@angular/core';
import * as XLSX from 'xlsx';
import * as XLS from 'xlsx';
@Injectable()
export class ExcelReaderService {
constructor() { }
importFromExcel(ev): JQueryPromise<any> {
let deferred = $.Deferred();
let regex = /^([a-zA-Z0-9\s_\\.\-:])+(.xlsx|.xls)$/;
let workbook;
let excelInJSON;
if (regex.test(ev.target.files[0].name.toString().toLowerCase())) {
let xlsxflag = false; /*Flag for checking whether excel is .xls format or .xlsx format*/
if (ev.target.files[0].name.toString().toLowerCase().indexOf(".xlsx") > 0) {
xlsxflag = true;
}
let fileReader = new FileReader();
fileReader.onload = (ev) => {
let binary = "";
let bytes = new Uint8Array((<any>ev.target).result);
let length = bytes.byteLength;
for (let i = 0; i < length; i++) {
binary += String.fromCharCode(bytes[i]);
}
/*Converts the excel data in to json*/
if (xlsxflag) {
workbook = XLSX.read(binary, { type: 'binary', cellDates: true, cellStyles: true });
// only first sheet
excelInJSON = XLSX.utils.sheet_to_json(workbook.Sheets[workbook.SheetNames[0]]);
deferred.resolve(excelInJSON);
}
else {
workbook = XLS.read(binary, { type: 'binary', cellDates: true, cellStyles: true });
excelInJSON = <{}[]>XLS.utils.sheet_to_row_object_array(workbook.Sheets[workbook.SheetNames[0]]);
deferred.resolve(excelInJSON);
}
}
// init read
if (xlsxflag)
fileReader.readAsArrayBuffer((<any>ev.target).files[0]);
else
fileReader.readAsBinaryString((<any>ev.target).files[0]);
} else {
deferred.reject('Invalid file!');
}
return deferred.promise();
}
}
در کامپوننت مصرفکننده
this.excelReaderService.importFromExcel(ev).then((result) => {
this.detailHeadings = Object.keys(result[0]);
this.detailData = result;
})
خیلی خوب خواهد بود اگر کسی به من در این زمینه کمک کند چون من تازه وارد برنامهنویسی ناهمزمان هستم.
پاسخ شماره ۱
این همان کاری است که من انجام دادم، در صورتی که کسی بخواهد یک سرویس Angular داشته باشد که فایلهای Excel را بخواند و با یک observable از محتوا به صورت JSON پاسخ دهد.
من از SheetJS برای خواندن فایل و خروجی گرفتن JSON استفاده میکنم.
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs/Observable';
import * as XLSX from 'xlsx';
@Injectable()
export class ExcelReaderService {
constructor() { }
importFromExcel(ev): Observable<any> {
let workbook;
let excelInJSON;
const fileReader = new FileReader();
// init read
fileReader.readAsArrayBuffer((<any>ev.target).files[0]);
return Observable.create((observer: Subscriber<any[]>): void => {
// if success
fileReader.onload = ((ev: ProgressEvent): void => {
let binary = "";
let bytes = new Uint8Array((<any>ev.target).result);
let length = bytes.byteLength;
for (let i = 0; i < length; i++) {
binary += String.fromCharCode(bytes[i]);
}
// Converts the excel data in to json
workbook = XLSX.read(binary, { type: 'binary', cellDates: true, cellStyles: true });
// only first sheet
excelInJSON = XLSX.utils.sheet_to_json(workbook.Sheets[workbook.SheetNames[0]]);
observer.next(excelInJSON);
observer.complete();
}
// if failed
fileReader.onerror = (error: FileReaderProgressEvent): void => {
observer.error(error);
}
});
}
}
از component، فقط رویداد را همانطور که در زیر نشان داده شده است به این سرویس ارسال کنید و آن با JSON پاسخ خواهد داد.
this.excelReaderService.importFromExcel(ev)
.subscribe((response: any[]): void => {
// do something with the response
});
پاسخ شماره ۲
همانطور که در مورد مشابه نشان داده شده است، به منظور اجتناب از الگوی (ضد) تأخیری، ابتدا باید رویداد load در FileReader به صورت Promise تبدیل شود:
let fileReader = new FileReader();
const fileReaderPromise = new Promise(resolve => fileReader.onload = resolve);
if (xlsxflag)
fileReader.readAsArrayBuffer((<any>ev.target).files[0]);
else
fileReader.readAsBinaryString((<any>ev.target).files[0]);
return fileReaderPromise.then(e => {
let excelInJSON;
...
return excelInJSON;
});
همچنین میتوان آن را با fromEvent به یک Observable تبدیل کرد:
const fileReader$ = Observable.fromEvent(fileReader, 'load')
.map(e => ...)
.first();
if (xlsxflag)
fileReader.readAsArrayBuffer((<any>ev.target).files[0]);
else
fileReader.readAsBinaryString((<any>ev.target).files[0]);
...
return fileReader$;
