宜春市网站建设_网站建设公司_在线客服_seo优化
2026/1/17 12:49:47 网站建设 项目流程

前端老炮的20G文件夹上传大冒险(附部分代码)

各位前端同仁们,我是老张,一个在辽宁苦哈哈写代码的"前端民工"。最近接了个活,客户要求用原生JS实现20G文件夹上传下载,还要支持IE9!这简直是要了我这把老骨头的命啊!不过为了那100块预算(划掉),为了技术理想,我决定拼了!

项目背景(哭诉版)

客户要求:

  • 20G大文件上传(这是要上传整个硬盘吗?)
  • 文件夹上传保留层级结构(1000个分类的文件,这是要搞文件管理系统吗?)
  • 支持SM4/AES加密(这是要搞国家机密吗?)
  • 断点续传(用户重启电脑都不能丢进度,这是要我做操作系统吗?)
  • 兼容IE9(现在还有人用IE9?哦,是Windows 7用户,好吧,我理解)
  • 预算100元以内(这连买杯咖啡都不够啊!)
  • 7*24小时技术支持(这是要把我当24小时客服吗?)
  • 3年免费维护(这是要签终身合同吗?)

技术选型(妥协版)

经过深思熟虑,我决定:

  • 前端:Vue3 + 原生JS(因为客户说不能用WebUploader)
  • 后端:SpringBoot(但我不写后端代码,哈哈)
  • 加密:crypto-js(因为IE9不支持Web Crypto API)
  • 文件分片:自己实现(因为找不到现成的IE9兼容方案)

部分前端代码(救命版)

以下是文件夹上传的核心代码,其他功能你们自己实现吧(逃):

20G文件夹上传神器(IE9兼容版) body { font-family: 'Microsoft YaHei', sans-serif; margin: 20px; } .progress-container { width: 100%; background: #f0f0f0; margin: 10px 0; } .progress-bar { height: 20px; background: #4CAF50; width: 0%; text-align: center; line-height: 20px; color: white; } .file-list { margin-top: 20px; border: 1px solid #ddd; padding: 10px; max-height: 300px; overflow-y: auto; } 20G文件夹上传神器(IE9兼容版) 选择文件夹 开始上传 暂停上传 0% 文件列表: // 全局变量(IE9兼容的"类"实现) var FileUploader = { files: [], chunkSize: 5 * 1024 * 1024, // 5MB分片 uploadId: null, paused: false, // 初始化(IE9兼容版) init: function() { document.getElementById('fileInput').addEventListener('change', this.handleFileSelect.bind(this)); // 模拟断点续传的本地存储(IE9用userData或cookie,这里简化用localStorage) if (!localStorage.getItem('uploadProgress')) { localStorage.setItem('uploadProgress', JSON.stringify({})); } }, // 处理文件夹选择(IE9兼容版) handleFileSelect: function(e) { this.files = []; var fileList = e.target.files; // 递归构建文件树(IE9没有File.webkitRelativePath,需要特殊处理) for (var i = 0; i < fileList.length; i++) { var file = fileList[i]; // 这里简化处理,实际需要解析webkitRelativePath或手动构建路径 // 兼容IE9的替代方案:让用户先压缩成zip...(开玩笑的) this.files.push({ file: file, path: file.name, // 实际应该用webkitRelativePath uploaded: 0, total: file.size, chunks: Math.ceil(file.size / this.chunkSize), uploadedChunks: 0 }); } this.renderFileList(); }, // 渲染文件列表(IE9兼容版) renderFileList: function() { var list = document.getElementById('fileList'); list.innerHTML = ''; for (var i = 0; i < this.files.length; i++) { var file = this.files[i]; var item = document.createElement('li'); item.innerHTML = file.path + ' (' + this.formatFileSize(file.uploaded) + '/' + this.formatFileSize(file.total) + ')'; list.appendChild(item); } }, // 格式化文件大小(IE9兼容版) formatFileSize: function(bytes) { if (bytes === 0) return '0 Bytes'; var k = 1024; var sizes = ['Bytes', 'KB', 'MB', 'GB']; var i = Math.floor(Math.log(bytes) / Math.log(k)); return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i]; }, // 开始上传(IE9兼容版) startUpload: function() { if (this.files.length === 0) { alert('请先选择文件夹'); return; } this.paused = false; this.uploadId = new Date().getTime(); // 简单生成上传ID // 加载之前的上传进度 var progressData = JSON.parse(localStorage.getItem('uploadProgress')); for (var i = 0; i < this.files.length; i++) { var file = this.files[i]; var fileId = file.path; // 实际应该用更唯一的ID // 如果有之前的上传记录,从断点继续 if (progressData[fileId] && progressData[fileId].uploadId === this.uploadId) { file.uploaded = progressData[fileId].uploaded; file.uploadedChunks = progressData[fileId].uploadedChunks; } else { progressData[fileId] = { uploadId: this.uploadId, uploaded: 0, uploadedChunks: 0 }; } } localStorage.setItem('uploadProgress', JSON.stringify(progressData)); this.uploadNextChunk(); }, // 上传下一个分片(IE9兼容版) uploadNextChunk: function() { if (this.paused) return; var fileToUpload = null; var fileIndex = -1; // 查找下一个需要上传的分片 for (var i = 0; i < this.files.length; i++) { var file = this.files[i]; if (file.uploadedChunks < file.chunks) { fileToUpload = file; fileIndex = i; break; } } if (!fileToUpload) { alert('上传完成!'); return; } var chunkIndex = fileToUpload.uploadedChunks; var start = chunkIndex * this.chunkSize; var end = Math.min(start + this.chunkSize, fileToUpload.total); var chunk = fileToUpload.file.slice(start, end); // 加密分片(IE9兼容版) var encryptedChunk = this.encryptData(chunk, 'secret-key-123'); // 实际应该用用户提供的密钥 // 模拟上传(实际应该用XMLHttpRequest或ActiveXObject for IE9) console.log('上传分片:', fileToUpload.path, '分片', chunkIndex, '/', fileToUpload.chunks); // 更新进度(IE9兼容版) fileToUpload.uploadedChunks++; fileToUpload.uploaded = end; // 保存上传进度 var progressData = JSON.parse(localStorage.getItem('uploadProgress')); var fileId = fileToUpload.path; progressData[fileId] = { uploadId: this.uploadId, uploaded: fileToUpload.uploaded, uploadedChunks: fileToUpload.uploadedChunks }; localStorage.setItem('uploadProgress', JSON.stringify(progressData)); // 更新UI this.renderFileList(); this.updateProgressBar(); // 继续上传下一个分片(使用setTimeout模拟异步) var self = this; setTimeout(function() { self.uploadNextChunk(); }, 100); // 模拟网络延迟 }, // 加密数据(IE9兼容版) encryptData: function(data, key) { // 实际应该用SM4或AES,这里简化用CryptoJS的AES // IE9需要额外处理ArrayBuffer到WordArray的转换 if (data instanceof Blob) { return new Promise(function(resolve) { var reader = new FileReader(); reader.onload = function(e) { var words = CryptoJS.enc.Latin1.parse(e.target.result); var encrypted = CryptoJS.AES.encrypt(words, key).toString(); resolve(encrypted); }; reader.readAsBinaryString(data); }); } // 简化处理,实际需要更复杂的实现 return CryptoJS.AES.encrypt(data, key).toString(); }, // 暂停上传(IE9兼容版) pauseUpload: function() { this.paused = true; alert('上传已暂停'); }, // 更新进度条(IE9兼容版) updateProgressBar: function() { var totalUploaded = this.files.reduce(function(sum, file) { return sum + file.uploaded; }, 0); var totalSize = this.files.reduce(function(sum, file) { return sum + file.total; }, 0); var percent = Math.round((totalUploaded / totalSize) * 100); document.getElementById('progressBar').style.width = percent + '%'; document.getElementById('progressBar').innerHTML = percent + '%'; } }; // 初始化(页面加载时执行) window.onload = function() { FileUploader.init(); // 兼容IE9的console.log(IE9没有console对象时) if (!window.console) { window.console = { log: function() {} }; } }; // 暴露全局方法(IE9兼容) window.startUpload = function() { FileUploader.startUpload(); }; window.pauseUpload = function() { FileUploader.pauseUpload(); };

开发心得(吐槽版)

  1. IE9兼容性:这简直是一场噩梦!没有File API,没有Promise,没有const/let,没有箭头函数…我感觉自己在用石器时代的工具开发火箭。

  2. 文件夹上传:现代浏览器有webkitRelativePath,但IE9没有。解决方案?要么让用户先压缩成zip,要么…算了,还是压缩吧(客户肯定不同意)。

  3. 断点续传:localStorage在IE9中只有5MB限制,存储大量上传进度?呵呵,祝你好运。

  4. 加密传输:IE9不支持Web Crypto API,只能用crypto-js这种老库,性能感人。

  5. 20G文件:分片上传是必须的,但IE9的内存管理…算了,别让浏览器崩溃就是胜利。

加入我们的接单群(广告版)

各位同仁,如果你也在为这些奇葩需求头疼,或者想找项目合作,欢迎加入我们的QQ群:374992201

群内福利:

  • 1~99元超级大红包(每天都有)
  • 接单合作机会
  • 技术交流分享
  • 推荐项目拿20%提成
  • 超级会员50%提成(躺着赚钱)

免责声明(求生版)

以上代码仅供娱乐参考,实际项目中请:

  1. 使用成熟的库(如WebUploader、Plupload等)
  2. 考虑使用Flash作为IE9的备选方案
  3. 对于20G文件,建议使用专业的大文件传输方案
  4. 加密传输请使用标准协议(如TLS)而非前端加密
  5. 断点续传最好由后端实现

最后,祝大家项目顺利,少遇奇葩需求!如果真有客户找上门,记得推荐给我啊(手动狗头)!

将组件复制到项目中

示例中已经包含此目录

引入组件

配置接口地址

接口地址分别对应:文件初始化,文件数据上传,文件进度,文件上传完毕,文件删除,文件夹初始化,文件夹删除,文件列表
参考:http://www.ncmem.com/doc/view.aspx?id=e1f49f3e1d4742e19135e00bd41fa3de

处理事件

启动测试

启动成功

效果

数据库

效果预览

文件上传

文件刷新续传

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

文件夹上传

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

下载示例

点击下载完整示例

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

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

立即咨询