javascript - 阅读器 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 是否除了这种类型的事件,如果是,是否存在适用于所有浏览器的通用事件?谢谢!
解决方案
问题更多在于其他浏览器并未真正正确支持此事件。
progess事件应该在读取操作期间触发,当一个新的 Blob 块已被读入内存但在读取器完成读取整个 Blob 之前,因此在它有任何可用结果之前。
- 如果
chunkPromise
使用done
属性为 false 且value
属性为对象的Uint8Array
对象来满足,请运行以下步骤:
[...]
3 - 如果自上次调用这些步骤以来已经过去了大约 50 毫秒,则将任务排队以触发调用的进度事件progress
fr。- 否则,如果
chunkPromise
满足了一个done
属性为 true 的对象,则将任务排队以运行以下步骤并中止此算法:
[...]
4 - 否则:
- 1 将 fr 设置
result
为result
。
如您所见,FileReader 的 ( fr )result
仅在 is 的属性时设置,done
即在名为progress 的事件被触发之后。chunkPromise
true
progress
如果您想访问读者的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,所以如果你要阅读非常大的文件,这是你需要处理的东西,但我把这个留给你作为练习。
推荐阅读
- visual-studio-code - VS Code:更改文件资源管理器中有问题的文件的前景色
- scala - 如果 scala 播放配置文件丢失,如何记录有用的错误?
- javascript - 使用 PHP 验证浏览器会话
- c# - [C#]:等待任务完成时达到 600 秒的最大超时
- xamarin - Xamarin Android Java 绑定:缺少类 (XMLReader)
- java - 批量执行不带参数的存储过程 Spring SimpleJdbcCall
- c++ - VS2017 中的 _crtBreakAlloc
- google-chrome-extension - 如何将 chrome 扩展连接到网络浏览器流量
- cumulocity - Cumulocity - 自定义小部件配置 - 多设备配置
- laravel - laravel_token 对第一个请求有效,但对后续请求无效