【问题标题】:Reader Onprogress Event Does Not Work on Firefox阅读器 Onprogress 事件在 Firefox 上不起作用
【发布时间】:2020-06-23 12:51:03
【问题描述】:

我正在使用 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 上的内容为空。为什么这仅在 Firefox 上不起作用? Firefox 是否除了这种类型的事件,如果是,是否存在适用于所有浏览器的通用事件?谢谢!

【问题讨论】:

    标签: javascript html


    【解决方案1】:

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

    progess 事件should fire during the read operation,当一个新的 Blob 块已被读入内存但阅读器完成读取整个 Blob 之前,因此在它之前任何结果可用。

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

    如您所见,FileReader 的 (fr) result 仅在 chunkPromisedone 属性为 true 时设置,即 after 名为progress 的进度事件被触发。

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

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

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

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

    幸运的是,TextDecoder API 能够读取数据流,这要归功于 stream member of its option parameter,这意味着使用此 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 engines do put a max length on strings,在 SpiderMonkey 中我认为它大约 1GB,但在 V8 中它只有 512MB,所以如果你要读取非常大的文件,这是你需要处理的东西,但我把它留给你作为练习。

    【讨论】:

    • 有道理,谢谢!我想使用进度,因为我不想读入整个文件,因为它可能非常大,所以我只想读到第一个进度事件。在不读取完整文件的情况下仅从进度中获取数据的正确方法是什么?
    • @JSONK。我确实用这样的流阅读器编辑了我的答案,并解释了为什么 FilerReader 在这里很合适。
    • 非常感谢!抱歉所有问题,但由于某种原因,该方法仅适用于 Firefox,不适用于其他浏览器。在 Safari 上试了一下,报错
    • 而且还能够在一小块之后停止它
    • @JSONK.,Safari 不支持 EventTarget 作为构造函数,你必须自己构建可选的 addEventListener 和 dispatchEvents,它也不支持 Blob.arrayBuffer,但你可以填充它使用新的 Response(this).arrayBuffer 真的很容易。这段代码在 FF 和 Chrome 中工作,但只是一个概念证明,核心是 Blob.slice 和 TextDecoder.decode(buf, {stream:true}) 得到广泛支持。实施取决于您,这不是 SO anwerers 的工作。
    猜你喜欢
    • 2019-06-20
    • 2019-07-21
    • 2018-01-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-03-28
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多