news 2025/12/22 22:10:16

JavaScript如何优化跨平台大文件上传的传输效率?

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
JavaScript如何优化跨平台大文件上传的传输效率?

广西IT软件公司大文件传输解决方案

作为广西IT行业软件公司项目负责人,针对产品部门提出的——100G级文件传输、断点续传稳定性、信创国产化适配、多技术栈兼容是核心痛点。结合公司现有JSP/SpringBoot技术栈与客户严格需求(非打包下载、SM4/AES加密、IE8兼容),我主导设计了一套全栈自研、源码级可控的大文件传输解决方案,以下从技术实现、集成方案、合规保障三方面展开说明,并提供关键代码示例:


一、方案设计核心要点

1. 功能全景覆盖

需求维度技术实现要点
100G文件传输分片上传(5MB/片)+ 断点续传(localStorage+数据库双持久化)+ 流式下载(分块读取)
文件夹层级保留现代浏览器webkitdirectory自动采集相对路径;IE8/9手动输入路径+后端路径映射表
断点续传稳定性进度信息同时存储于localStorage(前端)与upload_progress表(后端),双重校验
加密体系传输层HTTPS+存储层SM4(国密主用)/AES(可选)+ 密钥管理系统(KMS)集中管控
非打包下载生成文件列表,前端逐个请求下载链接(流式输出,避免服务器内存溢出)
多技术栈兼容前端封装为独立组件(支持Vue2/Vue3/React/JSP),后端提供RESTful API(无框架强绑定)
信创国产化适配支持达梦/人大金仓数据库、麒麟/统信OS、阿里云OSS私有云

2. 兼容性保障策略

  • 浏览器兼容
    • IE8/9:降级使用XMLHttpRequest+FormData(原生支持),手动输入文件夹路径(通过prompt采集),localStorage存储进度(IE8需引入es5-shim)。
    • 现代浏览器(Chrome/Firefox):利用File APIBlobslice等特性优化分片效率。
    • 信创浏览器(龙芯/红莲花):基于W3C标准实现,禁用浏览器私有特性,通过feature detection动态适配。
  • 操作系统适配
    • 前端代码通过Babel转译ES6+语法,兼容Windows 7+IE8至统信UOS最新版。
    • 后端Java代码无操作系统依赖,通过System.getProperty("os.name")动态适配文件路径分隔符(/\)。
  • 数据库适配
    • 使用SpringAbstractRoutingDataSource实现多数据源路由,支持MySQL/Oracle/达梦/人大金仓动态切换(配置文件指定db.type)。

3. 安全合规设计

  • 数据传输:强制HTTPS(TLS 1.2+),前端请求头添加X-Signature(SM3哈希+私钥签名防篡改)。
  • 数据存储:文件加密后存储至阿里云OSS私有桶,密钥由KMS管理(SM4密钥长度128bit,AES-256),密钥与文件哈希绑定存储。
  • 权限控制:集成集团LDAP/AD系统,通过@PreAuthorize注解实现文件操作权限校验(如“仅上传者可下载”)。

二、前端核心代码实现(Vue2兼容版)

1. 文件夹上传组件(支持层级结构+IE8)

// 兼容IE8的工具函数 if (!Array.prototype.forEach) { Array.prototype.forEach = function(fn) { for (var i=0; i<this.length; i++) fn(this[i], i); }; } if (!Object.keys) { Object.keys = function(obj) { return Object.getOwnPropertyNames(obj); }; } export default { data() { return { fileList: [], // 文件列表(含id、path、size、status、progress、chunks、uploadedChunks) chunkSize: 1024 * 1024 * 5, // 5MB/片(IE8内存限制) uploadUrl: '/api/upload/chunk', // 后端分片接口 statusText: { pending: '等待', uploading: '上传中', success: '成功', failed: '失败' }, isIeLegacy: /*@cc_on!@*/false // IE8检测 }; }, methods: { // 触发文件选择(兼容IE8) handleBrowse() { const input = document.createElement('input'); input.type = 'file'; input.style.display = 'none'; if (this.isIeLegacy) { // IE8不支持webkitdirectory,手动输入路径 this.$alert('请手动输入文件夹路径(如:D:/业务文件)', '提示', { confirmButtonText: '确定', callback: (action) => { if (action === 'confirm') { const path = prompt('请输入文件夹路径'); if (path) this.mockFolderFiles(path); // 模拟读取文件夹(实际需后端遍历) } } }); } else { input.setAttribute('webkitdirectory', ''); input.addEventListener('change', this.handleFileChange); } document.body.appendChild(input); input.click(); document.body.removeChild(input); }, // 现代浏览器文件夹处理 handleFileChange(e) { const files = e.target.files; if (!files.length) return; this.processFiles(files); }, // 递归遍历文件夹(现代浏览器) async processFiles(files) { const fileEntries = []; for (let i = 0; i < files.length; i++) { const file = files[i]; const entry = file.webkitGetAsEntry(); if (entry.isDirectory) { await this.traverseDirectory(entry, ''); } else { fileEntries.push({ name: file.webkitRelativePath, size: file.size, file: file }); } } this.generateHashes(fileEntries); // 生成文件哈希(断点续传校验) }, // 递归遍历目录(现代浏览器) traverseDirectory(entry, parentPath) { return new Promise((resolve) => { entry.file((file) => { const relativePath = parentPath + file.name; if (entry.isDirectory) { const dirReader = entry.createReader(); dirReader.readEntries((entries) => { entries.forEach((e) => this.traverseDirectory(e, `${relativePath}/`).then(resolve)); }); } else { this.fileList.push({ id: Date.now() + Math.random().toString(36).substr(2), path: relativePath, size: file.size, file: file, status: 'pending', progress: 0, chunks: Math.ceil(file.size / this.chunkSize), uploadedChunks: 0 }); } }); resolve(); }); }, // 生成文件哈希(SparkMD5,兼容IE8需引入兼容版本) generateHashes(files) { files.forEach((fileObj) => { const reader = new FileReader(); reader.onload = (e) => { const spark = new SparkMD5.ArrayBuffer(); spark.append(e.target.result); const hash = spark.end(); fileObj.hash = hash; this.checkUploadProgress(fileObj); // 检查后端进度 }; reader.readAsArrayBuffer(fileObj.file); }); }, // 检查断点续传进度(localStorage+后端双校验) async checkUploadProgress(fileObj) { // 从localStorage获取本地进度 const localProgress = localStorage.getItem(`upload_${fileObj.hash}`) || '{}'; const { uploadedChunks } = JSON.parse(localProgress); fileObj.uploadedChunks = uploadedChunks; // 从后端获取服务端进度(关键:刷新/关闭浏览器后恢复) try { const res = await this.$http.get(`/api/upload/check?hash=${fileObj.hash}`); fileObj.uploadedChunks = res.data.uploadedChunks || 0; localStorage.setItem(`upload_${fileObj.hash}`, JSON.stringify({ uploadedChunks: res.data.uploadedChunks })); } catch (err) { console.error('检查进度失败:', err); } if (fileObj.uploadedChunks >= fileObj.chunks) { fileObj.status = 'success'; fileObj.progress = 100; } else { this.startChunkUpload(fileObj); } }, // 分片上传(兼容IE8) startChunkUpload(fileObj) { fileObj.status = 'uploading'; for (let i = fileObj.uploadedChunks; i < fileObj.chunks; i++) { const start = i * this.chunkSize; const end = Math.min(start + this.chunkSize, fileObj.size); const chunk = fileObj.file.slice(start, end); // 前端加密(SM4示例,需后端配合密钥) const encryptedChunk = this.encryptChunk(chunk, fileObj.hash); // 构造FormData(IE8原生支持) const formData = new FormData(); formData.append('file', encryptedChunk); formData.append('hash', fileObj.hash); formData.append('chunk', i); formData.append('total', fileObj.chunks); formData.append('path', fileObj.path); // 上传请求(XMLHttpRequest兼容IE8) const xhr = new XMLHttpRequest(); xhr.open('POST', this.uploadUrl, true); xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest'); // 标识AJAX请求 xhr.upload.onprogress = (e) => { if (e.lengthComputable) { const progress = Math.round(((i + 1) / fileObj.chunks) * 100); fileObj.progress = progress; localStorage.setItem(`upload_${fileObj.hash}`, JSON.stringify({ uploadedChunks: i + 1 })); } }; xhr.onload = () => { if (xhr.status === 200) { fileObj.uploadedChunks++; if (fileObj.uploadedChunks === fileObj.chunks) { fileObj.status = 'success'; fileObj.progress = 100; localStorage.removeItem(`upload_${fileObj.hash}`); // 清除进度 this.mergeChunks(fileObj); // 触发合并 } } else { fileObj.status = 'failed'; this.$message.error(`分片${i}上传失败`); } }; xhr.onerror = () => { fileObj.status = 'failed'; this.$message.error('网络异常,上传中断'); }; xhr.send(formData); } }, // 合并分片(调用后端接口) mergeChunks(fileObj) { this.$http.post('/api/upload/merge', { hash: fileObj.hash, path: fileObj.path, totalChunks: fileObj.chunks }).then(() => { this.$message.success('文件上传完成'); }).catch((err) => { this.$message.error(`合并失败:${err.message}`); }); }, // SM4加密分片(需引入sm-crypto库) encryptChunk(chunk, fileHash) { const key = localStorage.getItem(`sm4_key_${fileHash}`) || 'default_sm4_key_128bit'; // 实际应从KMS获取 return smCrypto.sm4.encrypt(chunk, key); }, // 辅助函数:格式化文件大小 formatSize(size) { if (size >= 1024 ** 3) return `${(size / 1024 ** 3).toFixed(2)}GB`; if (size >= 1024 ** 2) return `${(size / 1024 ** 2).toFixed(2)}MB`; return `${(size / 1024).toFixed(2)}KB`; } } }; .file-upload { max-width: 1200px; margin: 20px auto; padding: 20px; border: 1px solid #ebeef5; border-radius: 4px; } .progress-table { width: 100%; border-collapse: collapse; margin-top: 15px; } .progress-table th, .progress-table td { padding: 12px; text-align: left; border-bottom: 1px solid #ebeef5; } .progress-bar { width: 200px; height: 20px; background: #f5f7fa; border-radius: 10px; overflow: hidden; } .progress { height: 100%; background: #409eff; transition: width 0.3s; } .status-uploading { color: #e6a23c; } .status-success { color: #67c23a; } .status-failed { color: #f56c6c; }

2. 非打包下载组件(支持100G级+50MB/S)

export default { data() { return { downloadList: [] // 文件列表(含id、path、size、url、hash) }; }, methods: { // 获取文件夹文件列表(调用后端接口) fetchFolderFiles(folderId) { this.$http.get(`/api/file/list?folderId=${folderId}`).then((res) => { this.downloadList = res.data.map((file) => ({ id: file.id, path: file.originalPath, size: file.size, url: `/api/file/download?hash=${file.hash}&path=${encodeURIComponent(file.originalPath)}`, hash: file.hash })); }); }, // 单个文件下载(流式输出,避免内存溢出) handleSingleDownload(file) { const a = document.createElement('a'); a.href = file.url; a.download = file.path.split('/').pop(); // 仅下载文件名(保留路径需后端配合) document.body.appendChild(a); a.click(); document.body.removeChild(a); }, // 批量下载(间隔500ms防拦截) handleBatchDownload() { if (this.downloadList.length === 0) { this.$message.warning('请先选择文件夹'); return; } this.downloadList.forEach((file, index) => { setTimeout(() => { this.handleSingleDownload(file); }, index * 500); }); }, // 辅助函数:格式化文件大小 formatSize(size) { if (size >= 1024 ** 3) return `${(size / 1024 ** 3).toFixed(2)}GB`; if (size >= 1024 ** 2) return `${(size / 1024 ** 2).toFixed(2)}MB`; return `${(size / 1024).toFixed(2)}KB`; } } };

三、后端核心代码实现(JSP/SpringBoot兼容)

1. 分片上传与断点续传接口(支持多数据库)

// 分片上传接口(JSP/SpringBoot通用)@WebServlet("/api/upload/chunk")publicclassUploadChunkServletextendsHttpServlet{protectedvoiddoPost(HttpServletRequestrequest,HttpServletResponseresponse)throwsServletException,IOException{// 1. 解析分片参数StringfileHash=request.getParameter("hash");intchunkIndex=Integer.parseInt(request.getParameter("chunk"));inttotalChunks=Integer.parseInt(request.getParameter("total"));Stringpath=request.getParameter("path");// 2. 解密分片(若启用加密)PartchunkPart=request.getPart("file");InputStreamchunkStream=chunkPart.getInputStream();if(EncryptionService.isEncrypted()){chunkStream=EncryptionService.decryptStream(chunkStream,fileHash);}// 3. 保存分片到临时目录(格式:{tempDir}/{fileHash}/{chunkIndex})StringtempDir=getServletContext().getRealPath("/temp")+"/"+fileHash;FiletempDirFile=newFile(tempDir);if(!tempDirFile.exists())tempDirFile.mkdirs();FilechunkFile=newFile(tempDirFile,String.valueOf(chunkIndex));Files.copy(chunkStream,chunkFile.toPath(),StandardCopyOption.REPLACE_EXISTING);// 4. 更新数据库分片状态(支持MySQL/达梦/人大金仓)UploadFilefile=UploadFileService.getOrCreate(fileHash,path,totalChunks);file.setUploadedChunks(file.getUploadedChunks()+1);UploadFileService.update(file);response.setStatus(HttpServletResponse.SC_OK);}}// 合并分片接口@WebServlet("/api/upload/merge")publicclassMergeChunkServletextendsHttpServlet{protectedvoiddoPost(HttpServletRequestrequest,HttpServletResponseresponse)throwsServletException,IOException{// 1. 解析合并参数StringfileHash=request.getParameter("hash");Stringpath=request.getParameter("path");inttotalChunks=Integer.parseInt(request.getParameter("total"));// 2. 校验所有分片是否上传完成UploadFilefile=UploadFileService.getByHash(fileHash);if(file.getUploadedChunks()<totalChunks){response.sendError(HttpServletResponse.SC_BAD_REQUEST,"分片未全部上传");return;}// 3. 合并分片到临时文件FilemergedFile=MergeService.mergeChunks(fileHash,totalChunks);// 4. 加密存储(SM4/AES)StringencryptedPath=StorageService.encryptAndStore(mergedFile,fileHash);// 5. 清理临时文件MergeService.cleanTempFiles(fileHash);// 6. 保存文件元数据file.setOssPath(encryptedPath);file.setStatus("COMPLETED");UploadFileService.update(file);response.setStatus(HttpServletResponse.SC_OK);}}

2. 非打包下载接口(流式输出)

// 下载接口(支持非打包)@WebServlet("/api/file/download")publicclassDownloadServletextendsHttpServlet{protectedvoiddoGet(HttpServletRequestrequest,HttpServletResponseresponse)throwsServletException,IOException{StringfileHash=request.getParameter("hash");Stringpath=URLDecoder.decode(request.getParameter("path"),StandardCharsets.UTF_8);// 1. 获取文件元数据UploadFilefile=UploadFileService.getByHash(fileHash);if(file==null){response.sendError(HttpServletResponse.SC_NOT_FOUND,"文件不存在");return;}// 2. 校验下载权限(集成LDAP/AD)if(!PermissionService.hasDownloadPermission(request,file)){response.sendError(HttpServletResponse.SC_FORBIDDEN,"无下载权限");return;}// 3. 流式输出文件(避免内存溢出)response.setContentType("application/octet-stream");response.setHeader("Content-Disposition","attachment; filename*=UTF-8''"+URLEncoder.encode(file.getOriginalPath(),StandardCharsets.UTF_8));try(InputStreamis=StorageService.download(file.getOssPath());OutputStreamos=response.getOutputStream()){byte[]buffer=newbyte[1024*1024];// 1MB缓冲区intlen;while((len=is.read(buffer))!=-1){os.write(buffer,0,len);os.flush();}}}}

3. 加密服务(SM4/AES双支持)

@ServicepublicclassEncryptionService{// SM4国密加密(BouncyCastle实现)publicstaticbyte[]sm4Encrypt(byte[]data,Stringkey)throwsException{Security.addProvider(newBouncyCastleProvider());Ciphercipher=Cipher.getInstance("SM4/ECB/PKCS5Padding","BC");SecretKeySpeckeySpec=newSecretKeySpec(key.getBytes(StandardCharsets.UTF_8),"SM4");cipher.init(Cipher.ENCRYPT_MODE,keySpec);returncipher.doFinal(data);}// SM4解密publicstaticbyte[]sm4Decrypt(byte[]data,Stringkey)throwsException{Security.addProvider(newBouncyCastleProvider());Ciphercipher=Cipher.getInstance("SM4/ECB/PKCS5Padding","BC");SecretKeySpeckeySpec=newSecretKeySpec(key.getBytes(StandardCharsets.UTF_8),"SM4");cipher.init(Cipher.DECRYPT_MODE,keySpec);returncipher.doFinal(data);}// 流式解密(支持大文件)publicstaticInputStreamdecryptStream(InputStreamencryptedStream,Stringkey)throwsException{Ciphercipher=Cipher.getInstance("SM4/ECB/PKCS5Padding","BC");SecretKeySpeckeySpec=newSecretKeySpec(key.getBytes(StandardCharsets.UTF_8),"SM4");cipher.init(Cipher.DECRYPT_MODE,keySpec);returnnewCipherInputStream(encryptedStream,cipher);}}

四、集成与合规保障

1. 现有系统集成方案

  • JSP项目:直接引入前端组件(Vue2打包后的静态资源),后端通过Servlet接口对接。
  • SpringBoot项目:将前端组件封装为Vue插件,通过vue-cli构建后发布至NPM,项目中通过npm install集成;后端提供RESTful API,与现有业务逻辑无缝衔接。
  • 数据库适配:通过application.properties动态配置db.type(mysql/oracle/dameng/kingbase),Spring自动加载对应驱动。

2. 信创环境适配

  • 数据库:提供达梦/人大金仓数据库驱动包,支持SQL语法兼容(如分页LIMIT改为ROWNUM)。
  • 操作系统:前端代码通过process.env.OS动态判断,后端使用File.separator适配路径分隔符。
  • 云存储:支持阿里云OSS私有云配置(oss.accessKeyId/oss.accessKeySecret动态注入)。

3. 授权与合作证明

  • 授权模式:提供无限授权(98万以内),包含源代码、技术文档、信创认证材料。
  • 合作证明:可提供5个央企/国企项目合同(含中国铁路、国家电网等)、软件著作权证书(登记号:2024SRXXXXXX)、信创环境认证书(达梦/麒麟等)。

五、技术支持与服务

1. 驻场培训

  • 提供3天现场培训(含源码解读、部署调试、常见问题排查),覆盖前端组件使用、后端接口调用、信创环境配置。

2. 7*24小时响应

  • 企业微信/电话支持,紧急问题2小时内定位,4小时内给出解决方案;重大问题(如服务器崩溃)提供现场驻场支持。

3. 版本迭代

  • 每年提供2次免费版本更新(含安全补丁、新功能),支持客户自定义需求定制(需签订补充协议)。

结语:本方案针对广西IT软件公司的实际需求设计,完全自主可控、源码级交付,满足100G文件传输、信创国产化、多浏览器兼容、SM4/AES加密等核心需求。前端通过原生JS+IE8兼容方案确保存量业务稳定,后端通过分片上传+流式下载实现高稳定性,云存储支持阿里云OSS私有云动态配置。作为集团级解决方案,本产品可直接集成至现有JSP/SpringBoot项目,降低维护成本。我们承诺提供源代码授权(预算98万以内)、专业技术支持与信创认证材料,助力公司快速落地金融级大文件传输需求。

导入项目

导入到Eclipse:点南查看教程
导入到IDEA:点击查看教程
springboot统一配置:点击查看教程

工程

NOSQL

NOSQL示例不需要任何配置,可以直接访问测试

创建数据表

选择对应的数据表脚本,这里以SQL为例

修改数据库连接信息

访问页面进行测试

文件存储路径

up6/upload/年/月/日/guid/filename

效果预览

文件上传

文件刷新续传

支持离线保存文件进度,在关闭浏览器,刷新浏览器后进行不丢失,仍然能够继续上传

文件夹上传

支持上传文件夹并保留层级结构,同样支持进度信息离线保存,刷新页面,关闭页面,重启系统不丢失上传进度。

批量下载

支持文件批量下载

下载续传

文件下载支持离线保存进度信息,刷新页面,关闭页面,重启系统均不会丢失进度信息。

文件夹下载

支持下载文件夹,并保留层级结构,不打包,不占用服务器资源。

下载示例

点击下载完整示例

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2025/12/13 14:44:13

TinyMCE5处理政府公文图片水印保留

企业网站后台Word粘贴与导入功能开发方案 方案概述 大家好&#xff0c;我是重庆某软件公司的ASP.NET前端工程师&#xff0c;最近接到了一个企业网站后台管理系统的增强需求&#xff0c;需要在TinyMCE编辑器中增加Word粘贴功能和多格式文档导入功能。经过一番研究和评估&#…

作者头像 李华
网站建设 2025/12/13 14:43:41

wangEditor处理ppt幻灯片图文混排转存站群

Word粘贴与导入功能集成方案评估与实施记录 一、需求分析与技术调研 作为江西某软件公司的前端工程师&#xff0c;我最近接到了在企业网站后台管理系统中集成Word粘贴和文档导入功能的需求。经过与客户的详细沟通&#xff0c;我梳理了以下核心需求点&#xff1a; Word粘贴功…

作者头像 李华
网站建设 2025/12/13 14:42:58

HunyuanVideo 1.5技术突破:83亿参数模型如何重塑视频内容产业链

腾讯混元团队最新开源的HunyuanVideo 1.5以83亿参数的轻量化架构实现专业级视频生成能力&#xff0c;在消费级GPU上完成720P视频创作&#xff0c;为中小企业提供了低门槛AI视频生成解决方案。该模型采用创新的SSTA稀疏注意力机制和3D VAE压缩技术&#xff0c;显存需求从60GB降至…

作者头像 李华
网站建设 2025/12/13 14:42:05

TinyMCE4支持跨平台excel数据绑定

没有任何限制的在任何产品中使用&#xff0c;完全开放产品源代码。 今儿一早&#xff0c;又有位网友“神通广大”地加了我微信&#xff0c;说是想探探这块技术的底儿&#xff0c;聊聊解决方案。原来&#xff0c;这位老兄也撞上了在富文本编辑器里粘贴Word图片自动上传的“小怪…

作者头像 李华
网站建设 2025/12/22 16:28:20

TinyMCE6导入ppt音视频自动转存链接

企业级Word内容导入解决方案技术评估报告 项目背景与需求综述 作为北京科技小巨人领军企业的项目负责人&#xff0c;我们在政务信息化建设领域承接了大量高规格项目。近期在政府公文系统、军工档案管理等项目中&#xff0c;客户对内容导入功能提出了更高要求&#xff0c;亟需…

作者头像 李华
网站建设 2025/12/19 6:55:38

moodycamel::ConcurrentQueue性能优化终极指南:解锁C++并发队列最佳实践

在现代多线程应用开发中&#xff0c;性能优化和并发控制是架构师面临的核心挑战。传统锁-based队列在高并发场景下常常成为系统瓶颈&#xff0c;而moodycamel::ConcurrentQueue作为工业级无锁并发队列&#xff0c;为C开发者提供了突破性的性能解决方案。本文将深入解析其核心原…

作者头像 李华