首页 > 解决方案 > 阅读器 Onprogress 事件在 Firefox 上不起作用

问题描述

我正在使用 onProgress 事件打开和查看第一个进度事件的文件。该代码适用于 Chrome 和 Safari,但在 Firefox 上我收到一条错误消息

TypeError: contents is null

这是我正在尝试做的以及在其他浏览器上有效的操作:

reader.onprogress = function() {
    var contents = this.result
    //do stuff with progress data

},false;
reader.readAsText( file );

在此进度事件期间,Firefox 上的 Contents 为空。为什么这仅在 Firefox 上不起作用?Firefox 是否除了这种类型的事件,如果是,是否存在适用于所有浏览器的通用事件?谢谢!

标签: javascripthtml

解决方案


问题更多在于其他浏览器并未真正正确支持此事件。

progess事件应该在读取操作期间触发当一个新的 Blob 块已被读入内存但读取器完成读取整个 Blob 之前,因此在它有任何可用结果之前。

  1. 如果chunkPromise使用done属性为 false 且value属性为对象的Uint8Array对象来满足,请运行以下步骤:
    [...]
    3 - 如果自上次调用这些步骤以来已经过去了大约 50 毫秒,则将任务排队以触发调用的进度事件progressfr。
  2. 否则,如果chunkPromise满足了一个done属性为 true 的对象,则将任务排队以运行以下步骤并中止此算法:
    [...]
    4 - 否则:
    • 1 将 fr 设置resultresult

如您所见,FileReader 的 ( fr )result仅在 is 的属性时设置,done即在名为progress 的事件被触发之后。chunkPromisetrueprogress

如果您想访问读者的result,请监听load事件,而不是progress.

如果您确实需要逐块读取此 Blob,则必须使用TextDecoder API构建自己的 FileReader 。

即使浏览器确实在进度事件中公开了内部缓冲数据,这也不是文本。包数据算法负责将字节数据实际转换为输出格式(此处为文本)。这仅在上述步骤 10.5.4 之前的两个子步骤中完成,仅当chunkPromise满足其done属性为 true的对象时。
换句话说,该过程首先将所有数据作为 ArrayBuffer 获取,然后将完整的 ArrayBuffer 处理为所需的任何输出格式。

鉴于 Unicode 文本编码是如何工作的,您甚至无法将 Blob 直接读取为您将使用创建的块的文本Blob.slice(),因为您很可能会落在组合字符边界的中间并破坏整个块。

对我们来说幸运的是,TextDecoder API 能够读取数据流,这要归功于stream它的 option 参数的成员,这意味着使用这个 API,我们可以向它传递数据块,并且它能够在不破坏字符的情况下读取它.

所以现在,我们要做的就是将 Blob 的块读取为 ArrayBuffers(使用Blob.arrayBuffer()很简单,但我们可以使用 FileReader 作为旧浏览器的后备),并在每个新块处触发进度事件。

class StreamTextReader extends EventTarget {
  constructor() {
    super();
    this.result = "";
  }
  async read( blob, chunksize = blob.size, encoding ) {
    const queueEvent = (name) => {
      const evt = new ProgressEvent( name );
      setTimeout( () => this.dispatchEvent( evt ) );
    };
    try {
      const decoder = new TextDecoder( encoding );
      this.result = "";
      let current_byte = 0;
      const last_byte = blob.size;

      while( current_byte < last_byte ) {

        const chunk = blob.slice( current_byte, current_byte + chunksize );
        const buf = await chunk.arrayBuffer();
        this.result += decoder.decode( buf, { stream: true } );

        current_byte += chunksize;
        queueEvent( 'progress' );
        
      }
      queueEvent( 'load' );
      return this.result;
     }
     catch( err ) {
      console.log(err);
      queueEvent( 'error' );
      throw err;
     }     
  }
}

const blob = new Blob( [ 'fooÀÂâà'.repeat( 10 ) ] );
const reader = new StreamTextReader();

reader.addEventListener('progress', (evt) => {
  console.log( "in progress", reader.result );
} );
reader.addEventListener('load', (evt) => {
  console.log( "in load", reader.result );
} );
reader.addEventListener('error', (evt) => {
  console.log( 'An error occured' );
} );
// read by chunks of 8 bytes
reader.read( blob, 8 );

为了证明 FileReader 无法处理这种流式处理,下面是读取同一个 Blob 的第一个块的结果:

const blob = new Blob( [ 'fooÀÂâà'.repeat( 10 ) ] );
const reader = new FileReader();

reader.addEventListener( 'load', (evt) => console.log( reader.result ) );

reader.readAsText( blob.slice( 0, 8 ) );

最后说明

注意javascript引擎确实在字符串上设置了最大长度,在SpiderMonkey中我认为它大约1GB,但在V8中它只有512MB,所以如果你要阅读非常大的文件,这是你需要处理的东西,但我把这个留给你作为练习。


推荐阅读