You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

JavaScript分片上传大文件内存占用过高(本应具备内存高效特性)

问题根因

  • 你的代码是并行发起所有XHR上传请求,for循环没有等待前一个请求完成就立刻读取下一个分片、发起新请求。所有分片对应的ArrayBuffer会被处于pending状态的XHR实例持有,无法被GC回收,直到请求完成才会释放引用,因此会同时在内存中堆积大量分片,最终占用接近整个文件的内存。
  • 你主动调用file.slice().arrayBuffer()会把分片内容完整读取到JS堆内存的ArrayBuffer对象中,而浏览器的自动GC有触发阈值,只有当内存占用达到对应阈值时才会自动执行,你连续快速创建大量大体积ArrayBuffer时,GC来不及触发就已经把全量分片加载到内存里了。
  • 另外XHR的onreadystatechange回调持有外部作用域的引用,也会一定程度上延缓相关对象的回收时机。

修复方案

你可以按优先级用以下方案优化:

  1. 直接传递Blob对象给XHR,不要转ArrayBuffer
    xhr.send()支持直接接收Blob类型,浏览器底层会自行处理流式读取,不会把整个分片内容加载到JS堆内存,内存占用可以直接降到分片大小级别。
  2. 改成串行上传,等前一个分片上传完成再处理下一个
    把XHR包装成Promise,每次循环await前一个请求完成再执行下一次,避免同时堆积大量请求和分片数据。
  3. 处理完的分片主动赋值为null,帮助GC更早标记可回收对象。

修复后的代码示例

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<form id="files">
    <input type="file" multiple id="file-input">
    <input type="submit">
</form>
<script>
    const form = document.getElementById('files');
    form.addEventListener('submit', async (e) => {
        e.preventDefault();
        const input = document.getElementById('file-input');
        const chunkSize = 100 * 1024 * 1024;
        for (let file of input.files) {
            let start = 0;
            const nChunks = Math.ceil(file.size / chunkSize);
            for (let i=0;i < nChunks; i++) {
                // 直接取Blob,不读ArrayBuffer
                const slice = file.slice(start, start + chunkSize);
                start += chunkSize;
                // 包装XHR为Promise,等待上传完成再走下一个
                await new Promise((resolve, reject) => {
                    const xhr = new XMLHttpRequest();
                    xhr.onreadystatechange = function(e) {
                        if (4 === this.readyState) {
                            console.log(`${i} xhr upload complete`, e);
                            resolve();
                        }
                    };
                    xhr.onerror = reject;
                    xhr.open('POST', '/test_memory');
                    // 直接传Blob
                    xhr.send(slice);
                })
            }
        }
    })
</script>
</body>
</html>

按以上方案修改后,内存会稳定保持在略高于单个分片大小的水平,不会随文件总大小上涨。

内容的提问来源于stack exchange,提问作者ratatatat

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.20 14:36:02