Angular:从FileReader

时间:2017-10-01 14:00:23

标签: angular typescript rxjs observable es6-promise

我试图从FileReader返回结果,然后我找到this实现。但由于它已经过时,我想知道如何使用ES6 Promises或Rx Observables来实现相同的功能。

以下是我的代码,参考上述链接,它按预期工作。

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();
  }

}

在消费者component

this.excelReaderService.importFromExcel(ev).then((result) => {
    this.detailHeadings = Object.keys(result[0]);
    this.detailData = result;
})

如果有人帮助我解决异步编程的问题,那就太棒了。

3 个答案:

答案 0 :(得分:6)

我就是这样做的,如果有人想要一个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
  });

答案 1 :(得分:4)

正如similar case中所示,为了避免延迟(反)模式,FileReader load事件应首先被宣传:

  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转换为可观察对象:

 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$;

答案 2 :(得分:0)

基于上述@karthikaruna的答案,我在Observables的基础上加以补充,很容易使.pipe链从File对象返回想要的内容:

(请注意,这些类型可能不是100%正确)。

import { Injectable } from '@angular/core';
import { Observable, Subscriber } from 'rxjs';
import { map } from 'rxjs/operators';
import * as XLSX from 'xlsx';

@Injectable()
export class ExcelReaderService {

    public importFromExcel(ev): Observable<any[]> {
        return this.fileToString(ev.target.files[0])
            .pipe(
                // convert from file contents to Excel rows
                map((binary: string): any[] => {
                    // Converts the excel data in to json
                    const workbook = XLSX.read(binary, { type: 'binary', cellDates: true, cellStyles: true });
                    // only first sheet
                    const excelInJSON = XLSX.utils.sheet_to_json(workbook.Sheets[workbook.SheetNames[0]]);
                    return excelInJSON;
                }),
            );
    } // end importFromExcel()

    private fileToString(file: File): Observable<string> {
        return Observable.create(
            (sub: Subscriber<string>): void => {
                const r = new FileReader;
                // if success
                r.onload = (ev: ProgressEvent): void => {
                    sub.next((ev.target as any).result);
                };
                // if failed
                r.onerror = (ev: FileReaderProgressEvent): void => {
                    sub.error(ev);
                };
                r.readAsText(file);
            }
        );
    } // end fileToString()

}