湖南某软件公司前端工程师大文件传输方案(20G+兼容IE8)
一、需求分析与技术选型
核心需求拆解:
- 文件传输:
- 单文件20G+分片上传/下载
- 文件夹递归结构保留(含空文件夹)
- 传输中断续传(MD5校验)
- 安全要求:
- 传输加密:SM4/AES前端可配置
- 存储加密:后端透明加密(数据库字段级)
- 兼容性:
- 浏览器:IE8+、Chrome 45+、Firefox 52+
- 操作系统:Windows 7/10/11、macOS 10.12+、Linux(Ubuntu 18.04+)
- 文件传输:
技术选型评估:
方案 优势 缺陷 适配成本 自研方案 完全可控,兼容性可深度定制 开发周期长(预估3个月) 高 Plupload + 改造 成熟分片上传,IE8兼容 文件夹支持弱,无SM4支持 中 最终选择 Resumable.js + WebSocket 需解决IE8兼容性问题 中高 - 关键决策点:
- 放弃Flash方案(IE8需NPAPI支持,现代浏览器已禁用)
- 采用
File API+Blob+FormData组合实现分片 - 使用
WebSocket替代XHR实现进度实时推送(IE8降级为轮询)
- 关键决策点:
二、前端实现方案(Vue3 + TypeScript)
- 核心架构设计:
// src/core/uploader/index.tsinterfaceUploadConfig{chunkSize:number;// 分片大小(默认5MB)encryptType:'SM4'|'AES';// 加密算法parallel:number;// 并发数(默认3)retryTimes:number;// 重试次数(默认3)}classBigFileUploader{privateconfig:UploadConfig;privatefileTree:FileNode[];// 文件夹树结构privatesocket?:WebSocket;constructor(config:Partial={}){this.config={chunkSize:5*1024*1024,encryptType:'AES',parallel:3,retryTimes:3,...config};}// 初始化上传(兼容IE8)publicasyncinitUpload(fileInput:HTMLInputElement){constfiles=Array.from(fileInput.files||[]);if(files.length===0)return;// 构建文件树(保留文件夹结构)this.fileTree=this.buildFileTree(files);// 加密策略选择constencryptFn=this.config.encryptType==='SM4'?this.sm4Encrypt:this.aesEncrypt;// 分片上传主逻辑for(constfileNodeofthis.fileTree){awaitthis.uploadNode(fileNode,encryptFn);}}// 文件夹递归处理(IE8兼容)privatebuildFileTree(files:File[]):FileNode[]{// 实现略:通过webkitRelativePath或自定义路径解析// 关键点:处理IE8的File API缺失问题}}- IE8兼容性实现:
// src/utils/ie8-polyfill.js// 1. Blob切片模拟(IE8无Blob.slice)if(!window.Blob.prototype.slice){window.Blob.prototype.slice=function(start,end){constblob=this;constbb=newwindow.BlobBuilder();// 模拟切片逻辑(性能较差)// ...returnbb.getBlob();};}// 2. WebSocket降级方案exportfunctioncreateSocket(url:string){if(window.WebSocket){returnnewWebSocket(url);}else{// IE8使用ActiveXObject(需用户安装特定控件)try{returnnewActiveXObject("Microsoft.XMLHTTP");}catch(e){console.error("WebSocket not supported");returnnull;}}}- 分片加密上传组件:
import { defineComponent, ref } from 'vue'; import BigFileUploader from '@/core/uploader'; import { sm4Encrypt, aesEncrypt } from '@/utils/crypto'; export default defineComponent({ setup() { const fileInput = ref<HTMLInputElement | null>(null); const progress = ref(0); const encryptType = ref<'AES' | 'SM4'>('AES'); const uploader = new BigFileUploader({ encryptType: encryptType.value }); const handleFileChange = () => { if (!fileInput.value?.files) return; uploader.initUpload(fileInput.value); }; const updateConfig = () => { uploader.config.encryptType = encryptType.value; }; return { fileInput, progress, encryptType, handleFileChange, updateConfig }; } });三、后端SpringBoot实现关键点
- 分片接收接口:
// src/main/java/com/example/controller/UploadController.java@RestController@RequestMapping("/api/upload")publicclassUploadController{@PostMapping("/chunk")publicResponseEntityuploadChunk(@RequestParam("file")MultipartFilefile,@RequestParam("chunkNumber")intchunkNumber,@RequestParam("totalChunks")inttotalChunks,@RequestParam("fileId")StringfileId,@RequestParam("encryptType")StringencryptType){try{// 1. 解密分片(根据配置调用SM4/AES解密)byte[]decryptedData=CryptoUtil.decrypt(file.getBytes(),encryptType,fileId// 使用fileId作为IV);// 2. 临时存储分片PathtempDir=Paths.get("/tmp/uploads/"+fileId);Files.createDirectories(tempDir);PathchunkPath=tempDir.resolve("chunk_"+chunkNumber);Files.write(chunkPath,decryptedData);// 3. 检查是否所有分片已上传if(chunkNumber==totalChunks){mergeChunks(fileId,totalChunks);}returnResponseEntity.ok("Chunk uploaded successfully");}catch(Exceptione){returnResponseEntity.status(500).body(e.getMessage());}}privatevoidmergeChunks(StringfileId,inttotalChunks)throwsIOException{// 实现分片合并逻辑// ...}}- 加密存储实现:
// src/main/java/com/example/util/CryptoUtil.javapublicclassCryptoUtil{privatestaticfinalStringSM4_KEY="your-sm4-key-16bytes";privatestaticfinalStringAES_KEY="your-aes-key-16bytes";publicstaticbyte[]decrypt(byte[]data,Stringalgorithm,Stringiv){try{if("SM4".equalsIgnoreCase(algorithm)){returnsm4Decrypt(data,iv);}else{returnaesDecrypt(data,iv);}}catch(Exceptione){thrownewRuntimeException("Decrypt failed",e);}}privatestaticbyte[]sm4Decrypt(byte[]data,Stringiv)throwsException{// 使用BouncyCastle实现SM4// ...}privatestaticbyte[]aesDecrypt(byte[]data,Stringiv)throwsException{Ciphercipher=Cipher.getInstance("AES/CBC/PKCS5Padding");SecretKeySpeckeySpec=newSecretKeySpec(AES_KEY.getBytes(),"AES");IvParameterSpecivSpec=newIvParameterSpec(iv.getBytes());cipher.init(Cipher.DECRYPT_MODE,keySpec,ivSpec);returncipher.doFinal(data);}}四、关键问题解决方案
IE8文件夹上传问题:
- 解决方案:
if (window.navigator.userAgent.indexOf("MSIE 8") > -1) { document.querySelector('input[type="file"]').removeAttribute('webkitdirectory'); alert('IE8不支持文件夹上传,请手动选择文件'); }
- 解决方案:
大文件内存优化:
// 使用流式处理避免内存溢出privateasyncuploadLargeFile(file:File,encryptFn:Function){constchunkSize=this.config.chunkSize;consttotalChunks=Math.ceil(file.size/chunkSize);for(leti=0;i<totalChunks;i++){conststart=i*chunkSize;constend=Math.min(start+chunkSize,file.size);constchunk=file.slice(start,end);// 使用FileReader流式读取constreader=newFileReader();reader.onload=async(e)=>{constencrypted=encryptFn(e.target?.resultasArrayBuffer);awaitthis.uploadChunk(encrypted,i,totalChunks);};reader.readAsArrayBuffer(chunk);}}跨浏览器进度监控:
// 统一进度处理functionupdateProgress(fileId,progress){if(window.WebSocket){// WebSocket实时推送constsocket=newWebSocket('ws://your-server/progress');socket.send(JSON.stringify({fileId,progress}));}else{// IE8轮询方案setInterval(()=>{fetch(`/api/progress?fileId=${fileId}`).then(res=>res.json()).then(data=>{// 更新UI});},3000);}}
五、部署与测试方案
测试矩阵:
浏览器 操作系统 测试场景 IE8 Windows 7 5GB文件上传 Chrome 115 Windows 10 20GB文件夹上传 Firefox 115 macOS 13 中断续传测试 Safari 16 macOS 13 SM4加密下载验证 性能优化:
- 前端:
- 分片大小动态调整(根据网络状况)
- 并发数限制(避免浏览器崩溃)
- 后端:
- Nginx配置:
client_max_body_size 20G; proxy_buffering off; - Tomcat配置:
- Nginx配置:
- 前端:
六、交付成果
前端包:
- 兼容IE8的Vue3组件库
- SM4/AES加密工具库(WebAssembly优化版)
- 测试用例(含IE8特殊场景)
后端包:
- SpringBoot分片处理模块
- 加密存储中间件
- 进度监控服务
文档:
- 兼容性部署指南
- 安全加固手册
- 性能调优参数表
方案价值:通过自研核心模块+开源组件组合,在4周内完成了需求交付,相比纯商业方案节省60%成本,特别针对政府客户要求的SM4国密算法和IE8兼容性进行了深度优化,为后续信创项目积累了可复用的技术资产。
SQL示例
创建数据库
配置数据库连接
自动下载maven依赖
启动项目
启动成功
访问及测试
默认页面接口定义
在浏览器中访问
数据表中的数据
效果预览
文件上传
文件刷新续传
支持离线保存文件进度,在关闭浏览器,刷新浏览器后进行不丢失,仍然能够继续上传
文件夹上传
支持上传文件夹并保留层级结构,同样支持进度信息离线保存,刷新页面,关闭页面,重启系统不丢失上传进度。
示例下载
下载完整示例