【问题标题】:Angular: FileReader - Access List from readerAngular:FileReader - 来自阅读器的访问列表
【发布时间】:2019-09-24 08:04:51
【问题描述】:

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

这一切都是通过这个输入完成的:

<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 显示数组,它看起来很好,现在唯一的事情是我想稍后在代码中使用它,因为我正试图返回它。 我试过这样的事情:

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

这不起作用。结果是undefined

我也尝试订阅数据:

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

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

类型“void”上不存在属性“subscribe”。

这就是我尝试实现 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);
    });
}

但我得到的唯一输出是:

可观察的 {_isScalar: false, _subscribe: ƒ}

我不太确定这意味着什么......

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

【问题讨论】:

  • 我认为你让它有点太复杂了。如果我写一个将内容列表存储在服务中的答案,可以吗?这样您就可以从代码中的任何位置访问它。 (我假设您的 readFolder 函数有效)
  • 没关系,我在想它有点复杂,但我真的很缺乏经验,所以我不确定是否有更简单的方法
  • 我唯一能说它有效的是readFile函数中的console.log,它完美地显示了我想要访问的数组
  • 最后你并没有把它弄得过于复杂,你已经非常接近一个可行的解决方案了。

标签: angular observable filereader


【解决方案1】:

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

1。使用 Angular CLI 生成服务

ng generate service read-folder

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

2。编写服务

您真正缺少的唯一难题是 FileReader 的 onload 是异步的,并且您不能(或不应该)从异步操作同步返回值(您必须“阻塞”您的应用程序同时读取文件,这是一个糟糕的用户体验)。相反,您应该 - 正如您所尝试的那样 - 使用来自 rxjs Angular 首选异步框架的 Observables。您可以创建一个Subject 并将.next(value) 发送给它,在您等待回复的地方您可以.subscribe(value =&gt; 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。

【讨论】:

  • 谢谢!我会尝试这个解决方案并希望它有效。我可能也会做的一个初学者错误是尝试将所有内容放在一个组件中,而不是实现不同的服务,这在更宏大的计划中似乎更有用......
  • 嗨!我在使用此服务时遇到了几个错误:codesandbox.io/s/angular-x5j9n (property) providedIn: string Expected 0 arguments, but got 1., Cannot find name 'Subject', Property 'files' does not exist on type 'ReadFolderService'.
  • @J.Doe 你似乎忘记了Subject 顶部的read-folder.service.ts 导入
  • 所以,我犯的错误比我预期的要多,但这是您的代码的固定示例codesandbox.io/s/angular-m86uk
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-10-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多