javascript - 以块或 ReadableStream 的形式发送 XMLHttpRequest 数据以减少大数据的内存使用
问题描述
我一直在尝试使用 JS 的XMLHttpRequest
Class 进行文件上传。我最初尝试过这样的事情:
const file = thisFunctionReturnsAFileObject();
const request = new XMLHttpRequest();
request.open('POST', '/upload-file');
const rawFileData = await file.arrayBuffer();
request.send(rawFileData);
上面的代码有效(耶!),并将文件的原始二进制数据发送到我的服务器。
但是......它使用了大量内存(因为整个文件都存储在内存中,而 JS 对内存不是特别友好)......我发现在我的机器上(16GB RAM),我不能t 发送大于 ~100MB 的文件,因为 JS 会分配太多内存,并且 Chrome 选项卡会因 SIGILL 代码而崩溃。
所以,我认为在这里使用 ReadableStreams 是个好主意。它在我的情况下具有足够好的浏览器兼容性(https://caniuse.com/#search=ReadableStream),我的 TypeScript 编译器告诉我 request.send(...) 支持 ReadableStreams (我后来得出结论,这是错误的)。我最终得到了这样的代码:
const file = thisFunctionReturnsAFileObject();
const request = new XMLHttpRequest();
request.open('POST', '/upload-file');
const fileStream = file.stream();
request.send(fileStream);
但是我的 TypeScript 编译器出卖了我(这很痛苦),我在我的服务器 ಠ_ಠ 上收到了“[object ReadableStream]”。
我还没有过多地探索上述方法,所以我不确定是否有办法做到这一点。我也非常感谢这方面的帮助!
将请求拆分成块将是一个最佳解决方案,因为一旦发送了一个块,我们就可以在收到整个请求之前将其从内存中删除。
我已经搜索和搜索,但还没有找到一种方法来做到这一点(这就是我在这里的原因......)。像这样的伪代码将是最佳的:
const file = thisFunctionReturnsAFileObject();
const request = new XMLHttpRequest();
request.open('POST', '/upload-file');
const fileStream = file.stream();
const fileStreamReader = fileStream.getReader();
const sendNextChunk = async () => {
const chunk = await fileStreamReader.read();
if (!chunk.done) { // chunk.done implies that there is no more data to be read
request.writeToBody(chunk.value); // chunk.value is a Uint8Array
} else {
request.end();
break;
}
}
sendNextChunk();
我希望此代码以块的形式发送请求,并在发送所有块时结束请求。
我尝试过的最有用的资源,但没有用:
没有工作,因为:
- 我需要解决方案来处理单个请求
- 我不能使用RTCDataChannel,它必须在一个普通的 HTTP 请求中(除了 XMLHttpRequest 还有其他方法吗?)
- 我需要它在现代 Chrome/Firefox/Edge 等中工作。(没有 IE 支持很好)
编辑:我不想使用多部分形式(FormData 类)。我想以块的形式发送从文件流中读取的实际二进制数据。
解决方案
你不能用 XHR afaik 做到这一点。但是更现代的fetch
APIReadableStream
确实支持为请求正文传递 a 。在你的情况下:
const file = thisFunctionReturnsAFileObject();
const response = await fetch('/upload-file', {
method: 'POST',
body: file.stream(),
});
但是,我不确定这是否真的会使用分块编码。
推荐阅读
- c# - 如何从我的模型中的 sqllite db 获取数据?
- authentication - ajax 调用后的 Elixir 会话数据
- python - 使用中心轴更改散点图中原点的坐标
- python - 工具 pylint 无法运行(引发异常)Python
- java - 从实现 Runnable 的方法返回的捕获响应
- vue.js - 无法将我的数据传递给 mount() 方法
- c - 为什么我的音频输出在每个周期增加 100Hz?
- php - WooCommerce:检查购物车项目是否免费并将类添加到表格行
- c# - 不能将 $count 与 ODataRoute 属性一起使用
- sql - 如何检查一个序列是否高于某个数字,如果不在 Postgres 中更改它