无法上传大于500MB的.tar.gz文件,求JavaScript客户端解决方案
大文件上传解决方案(500MB+ .tar.gz)
你的代码崩溃的核心原因是一次性将整个大文件加载到内存,readFile会把文件全部读入内存,500MB以上的文件会直接耗尽客户端内存导致崩溃。另外,请求头里的Access-Control-Allow-Origin、Access-Control-Allow-Methods、Access-Control-Allow-Credentials是服务端响应头,客户端请求时不需要设置,直接删掉即可。
以下是针对不同场景的解决方案:
一、Electron环境(支持文件路径读取)
如果你的客户端是Electron应用,可以通过创建文件流实现流式上传,避免加载整个文件到内存:
const fs = require('fs'); const { Readable } = require('stream'); const axios = require('axios'); async uploadMapFile(url, filePath, mapType) { try { const contentType = mapType === "navi" ? "application/zip" : "application/tar+gzip"; // 创建文件可读流 const fileStream = fs.createReadStream(filePath); // 转换为axios支持的Web流格式 const stream = Readable.toWeb(fileStream); const { data } = await axios.put(url, stream, { headers: { "Content-Type": contentType }, // 禁用自动转换,确保流直接发送 transformRequest: [(data) => data] }); console.log("文件上传完成", data, mapType); await this.afterFileUpload(mapType); this.store.mapUploadFlag = false; } catch (error) { console.error("上传失败", error); this.store.mapUploadFailed = true; } },
这种方式会逐块读取文件并发送,内存占用始终保持在较低水平。
二、纯浏览器环境(仅支持用户选择的File对象)
浏览器出于安全限制,无法直接通过文件路径读取文件,必须通过用户选择(<input type="file">)获取File对象。此时可以通过分片上传实现大文件传输:
async uploadMapFile(url, file, mapType) { try { const contentType = mapType === "navi" ? "application/zip" : "application/tar+gzip"; const chunkSize = 10 * 1024 * 1024; // 10MB分片大小 const totalChunks = Math.ceil(file.size / chunkSize); for (let chunkIndex = 0; chunkIndex < totalChunks; chunkIndex++) { const start = chunkIndex * chunkSize; const end = Math.min(start + chunkSize, file.size); // 分割文件为当前分片 const chunk = file.slice(start, end); // 上传分片,需服务端支持分片接收与合并 const { data } = await axios.put( `${url}?chunkIndex=${chunkIndex}&totalChunks=${totalChunks}`, chunk, { headers: { "Content-Type": contentType, "Content-Range": `bytes ${start}-${end-1}/${file.size}` // 告知服务端分片范围 } } ); console.log(`分片 ${chunkIndex+1}/${totalChunks} 上传完成`, data); } // 所有分片上传完成后,通知服务端合并文件 await axios.post(`${url}/merge`, { fileName: file.name, totalChunks }); console.log("文件上传完成", mapType); await this.afterFileUpload(mapType); this.store.mapUploadFlag = false; } catch (error) { console.error("上传失败", error); this.store.mapUploadFailed = true; } },
使用说明:
- 调用时传入用户选择的
File对象(而非文件路径):// 示例:input选择文件后获取File对象 document.querySelector('input[type="file"]').addEventListener('change', async (e) => { const file = e.target.files[0]; await uploadMapFile('你的上传URL', file, 'tar.gz'); }); - 服务端需要配合实现分片接收、存储与合并逻辑,否则无法完成最终文件组装。
关键注意事项
- 流式/分片上传的核心是避免一次性加载整个文件到内存,始终处理小块数据。
- 浏览器端无法绕过安全限制直接读取本地文件路径,必须通过用户交互获取
File对象。 - 上传大文件时,建议添加进度监听(axios可通过
onUploadProgress回调实现),提升用户体验。
内容的提问来源于stack exchange,提问作者Nilesh Mishra
相关产品推荐
相关产品推荐

