Angular:FileReader - 来自 reader 的访问列表

Angular: FileReader - Access List from reader

我正在尝试通过输入读取文件夹并将文件夹中包含的每个单独文件的内容保存在一个数组中。

输入完成:

<div class="form-group">
        <input type="file" id="file" (change)="accessFiles($event)" webkitdirectory directory multiple >
</div>

还有这个 FileReader:

files = []
readFolder(fileChangeEvent: Event) {
    this.readFile(0, (fileChangeEvent.target as HTMLInputElement).files);
 }

private readFile(index, files) {
     const reader = new FileReader();
     if (index >= files.length ) {
       console.log(this.files);
       return;
     }
     const file = files[index];
     reader.onload = (e: any) => {
       const bin = e.target.result;
       this.files.push(e.target.result);
       this.readFile(index + 1, files);
     };
       reader.readAsBinaryString(file);
}

现在,readFile 中的 console.log 显示数组,它看起来不错,现在唯一的问题是我想稍后在代码中使用它,为此我正在尝试 return它。 我试过这样的事情:

accessFiles(data){
    const contentList =  this.readDocument(data));
}

这没有用。输出结果是 undefined

我也试过订阅数据:

accessFiles(data){
     this.readFolder(data).subscribe(values => {
    }
}

但后来我得到了这个错误:

Property 'subscribe' does not exist on type 'void'.

这就是我尝试实现 Observable 的原因:

files = []
readFolder(fileChangeEvent: Event) {
    return this.readFile(0, (fileChangeEvent.target as HTMLInputElement).files);
 }

private readFile(index, files) {
    return new Observable<any>(obs => {
     const reader = new FileReader();
     if (index >= files.length ) {
       console.log(this.files);
       obs.next({result: this.files});
       return;
     }
     const file = files[index];
     reader.onload = (e: any) => {
       const bin = e.target.result;
       this.files.push(e.target.result);
       this.readFile(index + 1, files);
     };
       reader.readAsBinaryString(file);
    });
}

但我从中得到的唯一输出是:

Observable {_isScalar: false, _subscribe: ƒ}

我不太确定那是什么意思...

如何从 FileReader 正确访问列表?或者我做错了什么?

像这样的事情最好在服务内部完成,您可以在服务内部创建函数来随时获取、存储和 return 文件。然后将该服务注入组件的构造函数或其他需要该服务的地方。

1。使用 Angular CLI

生成服务
ng generate service read-folder

这将创建一个名为 read-folder.service.ts 的文件,其中包含一个名为 ReadFolderService 的服务,并将其注册到您的根模块中。

2。编写服务

您真正遗漏的唯一难题是 FileReader 的加载是异步的,并且您不能(或不应该)同步 return 来自异步操作的值(您在读取文件时必须 "block" 您的应用程序,这是一种糟糕的用户体验)。相反,您应该——正如您所尝试的那样——使用来自 rxjs Angular 的首选异步框架的 Observables。您可以创建一个 Subject 并将 .next(value) 发送给它,在等待回复的地方您可以 .subscribe(value => function) 给它。

import { Injectable } from "@angular/core";
import { Subject } from "rxjs";

export class ReadFolderService {
  private fileCache: string[];
  private folderReader$: Subject<string[]> = new Subject();

  public readFolder(files: string[]) {
    this.fileCache = [];
    this.readFile(0, files);
    return this.folderReader$.asObservable(); // I don't return the subject itself, but a "readonly" Observable.
  }

  private readFile(index, files) {
    const reader = new FileReader();
    if (index >= files.length) {
      this.folderReader$.next(this.fileCache);
      return;
    }
    const file = files[index];
    reader.onload = (e: any) => {
      this.fileCache.push(e.target.result);
      this.readFile(index + 1, files);
    };
    reader.readAsBinaryString(file);
  }
}

3。注入服务,调用,订阅结果

您的组件内部:

import { ReadFolderService } from 'src/app/read-folder.service';

...

class MyComponent {

  // Angular will inspect the arguments of your constructor and make the service for you.
  // This is called "dependency injection"
  construct(private folderReader: ReadFolderService) { }

  readFolder(fileChangeEvent: Event) {
     this.folderReader.readFolder(fileChangeEvent.target as HTMLInputElement).files)
                      .subscribe( fileContents => {
                         // Do something
                      });
  }
}

如果需要,您还可以向该服务添加另一个功能,以使用私有 fileCache 值检索上次读取的文件。但是您的递归解决方案可能会导致您在读取另一个文件夹时访问 fileCache。