乌鲁木齐市网站建设_网站建设公司_HTML_seo优化
2025/12/20 13:03:05 网站建设 项目流程

《大文件传输系统开发手记:一个老程序员的求生指南》


一、项目背景

最近接了个"地狱级"外包:客户要我用原生JS实现20G文件夹上传/下载,还要兼容IE9!我摸着所剩无几的头发,看着100元预算,陷入了沉思…

客户核心需求

  1. 支持20G文件夹上传(含1000级子目录)
  2. 断点续传(重启电脑都不能丢进度)
  3. SM4/AES加密传输+存储
  4. 非打包下载(几万文件秒传)
  5. 兼容IE9到Chrome全系列
  6. 预算100元(买杯咖啡都不够)

二、技术选型(穷人版)

前端方案
  • 原生JS+递归算法:手动实现文件夹树遍历
  • Web Workers:防止IE9卡死
  • Blob分片:把20G切成5MB小饼干
后端方案
  • SpringBoot:白嫖的Tomcat容器
  • MySQL:记录上传进度(穷人的Redis)
  • SM4算法:用BouncyCastle库(免费!)
加密方案
// 前端AES加密(兼容IE9)functionencryptAES(data,key){// 兼容IE9的crypto-js降级方案if(typeofCryptoJS==='undefined'){alert('请安装crypto-js插件!');returndata;}returnCryptoJS.AES.encrypt(data,key).toString();}

三、核心代码实现

1. 文件夹上传(原生JS版)
// 递归扫描文件夹(IE9兼容版)functionscanFolder(entry,pathMap){if(entry.isFile){entry.file(file=>{constrelativePath=pathMap.join('/')+'/'+file.name;uploadFile(file,relativePath);// 调用分片上传});}elseif(entry.isDirectory){constdirReader=entry.createReader();dirReader.readEntries(entries=>{constnewPath=[...pathMap,entry.name];entries.forEach(e=>scanFolder(e,newPath));});}}// 初始化文件夹选择(IE9兼容)document.getElementById('folderInput').addEventListener('change',e=>{constfiles=e.target.files;if(files.length===0)return;// IE9的特殊处理if(window.FileReader&&!window.FileEntry){alert('请使用Chrome/Firefox上传文件夹!');return;}// 现代浏览器if(files[0].webkitRelativePath){constrootPath=files[0].webkitRelativePath.split('/')[0];Array.from(files).forEach(file=>{constpath=file.webkitRelativePath.replace(rootPath,'');uploadFile(file,path);});}// 通过input[webkitdirectory]选择elseif(e.target.webkitEntries){Array.from(e.target.webkitEntries).forEach(entry=>{scanFolder(entry,[]);});}});
2. 分片上传(断点续传)
// 上传文件分片asyncfunctionuploadChunk(file,chunkIndex,totalChunks,filePath,fileId){constchunkSize=5*1024*1024;// 5MBconststart=chunkIndex*chunkSize;constend=Math.min(file.size,start+chunkSize);constchunk=file.slice(start,end);// 加密分片(SM4)constencrypted=awaitencryptSM4(chunk,'1234567890abcdef');// 实际应从后端获取密钥returnfetch('/api/upload',{method:'POST',body:encrypted,headers:{'X-File-ID':fileId,'X-Chunk-Index':chunkIndex,'X-Total-Chunks':totalChunks,'X-File-Path':filePath}}).then(res=>res.json());}// 进度持久化(localStorage+IndexedDB双备份)functionsaveProgress(fileId,progress){try{// IE9兼容方案if(window.localStorage){localStorage.setItem(`progress_${fileId}`,JSON.stringify(progress));}// 现代浏览器用IndexedDBif(window.indexedDB){constrequest=indexedDB.open('FileProgressDB',1);request.onsuccess=()=>{constdb=request.result;consttx=db.transaction('progress','readwrite');conststore=tx.objectStore('progress');store.put(progress,fileId);};}}catch(e){console.error('进度保存失败:',e);}}
3. SpringBoot后端(穷人版)
@RestController@RequestMapping("/api")publicclassFileController{@Value("${file.storage.path}")privateStringstoragePath;// 分片上传接口@PostMapping("/upload")publicResponseEntityuploadChunk(@RequestParam("file")MultipartFilefile,@RequestHeader("X-File-ID")StringfileId,@RequestHeader("X-Chunk-Index")intchunkIndex,@RequestHeader("X-Total-Chunks")inttotalChunks,@RequestHeader("X-File-Path")StringfilePath){try{// 解密文件(SM4)byte[]decrypted=decryptSM4(file.getBytes(),"1234567890abcdef");// 保存分片StringchunkPath=storagePath+"/"+fileId+"/chunks/"+chunkIndex;Files.createDirectories(Paths.get(chunkPath).getParent());Files.write(Paths.get(chunkPath),decrypted);// 更新进度到MySQLupdateProgress(fileId,chunkIndex,totalChunks,filePath);returnResponseEntity.ok().body(Map.of("success",true));}catch(Exceptione){returnResponseEntity.status(500).body(Map.of("error",e.getMessage()));}}// 合并文件(伪代码)@GetMapping("/merge")publicResponseEntitymergeFile(@RequestParamStringfileId){// 1. 从MySQL查询所有分片信息// 2. 按顺序合并到最终文件// 3. 删除分片目录// 4. 返回下载URLreturnResponseEntity.ok().body(Map.of("url","/download/"+fileId));}}

四、兼容性黑科技

1. IE9兼容方案
2. 文件夹上传降级方案
// 如果浏览器不支持文件夹上传,提示用户压缩成ZIPfunctioncheckFolderSupport(){if(!window.File&&!window.FileReader&&!window.FileList&&!window.Blob){alert('您的浏览器太古老了!请:\n1. 使用Chrome/Firefox\n2. 或把文件夹压缩成ZIP上传');}}

五、部署指南(白嫖版)

  1. 服务器配置

    • 买台1核2G的云服务器(阿里云学生机9.9元/月)
    • 安装Tomcat + MySQL
  2. 编译打包

    # 前端打包npmrun build# 后端打包mvn clean package# 手动复制dist目录到Tomcat的webappscp-r dist/* /var/lib/tomcat9/webapps/ROOT/
  3. MySQL初始化

CREATETABLEfile_progress(idVARCHAR(64)PRIMARYKEY,file_pathVARCHAR(512)NOTNULL,total_chunksINTNOTNULL,received_chunksINTDEFAULT0,last_updateTIMESTAMPDEFAULTCURRENT_TIMESTAMPONUPDATECURRENT_TIMESTAMP);

六、项目总结

  1. 成果:用100元预算实现了20G文件夹上传!
  2. 代价:头发又少了100根…
  3. 建议:下次接单前先看牙医(防止气到咬碎后槽牙)

最终解决方案

  • 前端代码:GitHub链接
  • 后端代码:附在邮件压缩包里
  • 交流群:374992201(加群领红包!)

温馨提示:本项目仅供学习交流,如需商用请自行购买商业授权(虽然我根本没卖…)

导入项目

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

工程

NOSQL

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

创建数据表

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

修改数据库连接信息

访问页面进行测试

文件存储路径

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

效果预览

文件上传

文件刷新续传

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

文件夹上传

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

下载示例

点击下载完整示例

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询