如何用JavaScript分块下载30GB大文件(无需Node.js)
解决方案:浏览器端分块流式下载并追加写入本地文件
可以不使用Node.js实现这个需求,核心是利用浏览器的FileSystem Access API——它允许网页在用户授权后直接操作本地文件,支持追加写入;同时结合流式处理响应体,避免将大文件块全部加载到内存中。
原代码的问题
你的现有代码会同时发起30个请求,把所有1GB的文件块都存在chunks数组里,等全部下载完才生成文件,这会导致浏览器内存占用飙升(最多达30GB),必然引发崩溃。正确的做法是串行下载分块,每下载一块就写入本地,写完后释放内存,再继续下一块。
实现代码
document.addEventListener("DOMContentLoaded", function () { const DOWNLOAD_URL = "你的30GB文件URL"; const SLICE_SIZE = 1 * 1024 * 1024 * 1024; // 1GB分块 async function downloadAndAppendChunk(fileHandle, chunkIndex, totalChunks) { const start = chunkIndex * SLICE_SIZE; // 最后一块的end设为'',让服务器返回剩余所有字节 const end = chunkIndex === totalChunks - 1 ? '' : start + SLICE_SIZE - 1; try { const response = await fetch(DOWNLOAD_URL, { headers: { 'Range': `bytes=${start}-${end}` } }); if (!response.ok) { throw new Error(`下载分块 ${chunkIndex + 1} 失败,状态码:${response.status}`); } if (response.status !== 206 && chunkIndex !== totalChunks - 1) { throw new Error("服务器不支持Range请求,无法分块下载"); } // 获取文件写入流,设置为追加模式 const writable = await fileHandle.createWritable({ keepExistingData: true }); // 流式读取响应体,边读边写入 const reader = response.body.getReader(); let done, value; while ({ done, value } = await reader.read()) { if (done) break; await writable.write(value); // 释放当前缓冲区内存 value = null; } await writable.close(); console.log(`分块 ${chunkIndex + 1}/${totalChunks} 下载并写入完成`); return true; } catch (error) { console.error(`分块 ${chunkIndex + 1} 处理失败`, error); return false; } } async function startDownload() { try { // 先请求用户授权,选择要保存的文件(会创建新文件或覆盖已有文件) const fileHandle = await window.showSaveFilePicker({ suggestedName: "large-file.bin", types: [{ description: "Binary File", accept: { "application/octet-stream": [".bin"] } }] }); // 先获取文件总大小(可选,用来计算总分块数) const headResponse = await fetch(DOWNLOAD_URL, { method: 'HEAD' }); const totalSize = parseInt(headResponse.headers.get('Content-Length')); const totalChunks = Math.ceil(totalSize / SLICE_SIZE); console.log(`文件总大小:${totalSize}字节,共${totalChunks}个分块`); // 串行下载每个分块 for (let i = 0; i < totalChunks; i++) { const success = await downloadAndAppendChunk(fileHandle, i, totalChunks); if (!success) { // 可添加重试逻辑 alert(`分块 ${i + 1} 下载失败,已终止`); break; } } alert("文件下载完成!"); } catch (error) { console.error("下载流程失败", error); alert(`下载出错:${error.message}`); } } document.getElementById("downloadLink1").addEventListener("click", function (event) { event.preventDefault(); startDownload(); }); });
关键细节说明
- FileSystem Access API授权:必须通过
showSaveFilePicker让用户主动选择保存位置,这是浏览器的安全限制,无法静默写入本地文件。 - 流式处理:使用
response.body.getReader()流式读取响应体,每次只加载一小段数据到内存,写完就释放,避免1GB块占用内存。 - Range请求优化:最后一块的
end设为空字符串,让服务器返回剩余所有字节,无需精确计算最后一块的大小。 - 串行下载:用
for循环+await确保完成一块再下载下一块,避免并发请求导致的内存和网络压力。
兼容性注意
- Chrome/Edge 86+ 原生支持此API
- Firefox 需要在
about:config中开启dom.fs.enabled和dom.fs.writable_files.enabled - Safari 15.4+ 部分支持,可能存在兼容性问题
内容的提问来源于stack exchange,提问作者deepak vajpayee
相关产品推荐
相关产品推荐

