news 2026/5/28 8:52:55

跨平台网页应用如何用vue实现文件夹上传?

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
跨平台网页应用如何用vue实现文件夹上传?

前端老炮的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

处理事件

启动测试

启动成功

效果

数据库

效果预览

文件上传

文件刷新续传

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

文件夹上传

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

下载示例

点击下载完整示例

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

我让AI读了1000个GitHub测试项目,总结出“最佳实践”

‌一、测试工程的四大支柱‌基于对1000 GitHub 测试项目、科技巨头公开文档及行业实践的深度分析&#xff0c;软件测试的最佳实践已形成清晰的四维框架&#xff1a;维度核心实践代表项目/工具关键价值‌测试架构‌测试金字塔&#xff08;80%单元 15%集成 5%E2E&#xff09;Go…

作者头像 李华
网站建设 2026/5/22 13:24:45

为什么AI生成的测试用例比人工更“刁钻”?

重新定义“刁钻”测试用例 在软件测试领域&#xff0c;“刁钻”测试用例特指那些能有效暴露隐藏缺陷、覆盖边缘场景的用例&#xff0c;它们往往超出常规逻辑&#xff0c;挑战系统极限。传统人工测试依赖于测试工程师的经验和直觉&#xff0c;但受限于认知偏差和时间压力&#…

作者头像 李华
网站建设 2026/5/20 14:25:17

计算机视觉与机器学习在语音交互中的应用

Alexa & Friends 特邀 Pradeep Natarajan&#xff0c;Alexa AI 首席应用科学家 2021年10月28日&#xff0c;某中心 Alexa AI 团队的首席应用科学家 Pradeep Natarajan 加入了首席 Alexa 技术推广专家 Jeff Blankenburg 的播客节目《Alexa & Friends》&#xff0c;讨论了…

作者头像 李华
网站建设 2026/5/25 10:26:04

Spring Boot 中使用 JSONPath 高效处理 JSON 数据

前言在日常开发中&#xff0c;我们经常需要处理 JSON 数据&#xff0c;特别是从复杂的 JSON 结构中提取特定字段。传统的处理方式如 Gson、Jackson 的 API 虽然功能强大&#xff0c;但在处理复杂路径提取时代码往往显得冗长且不易维护。今天给大家介绍一个更优雅的解决方案 ——…

作者头像 李华
网站建设 2026/5/26 1:00:44

MySQL自增id超过int最大值的场景

点击标题下「蓝色微信名」可快速关注 数据库的主键我们有时候会用自增列&#xff0c;但是自增都会有个上限&#xff0c;如果达到怎么办&#xff1f;技术社群的这篇文章《MySQL自增id超过int最大值怎么办&#xff1f;》就给我们讲解了MySQL数据库自增列达到上限该怎么办&#xf…

作者头像 李华
网站建设 2026/5/27 19:34:30

ssm651网上鲜花店网站vue

目录网上鲜花店网站&#xff08;Vue框架&#xff09;摘要开发技术源码文档获取/同行可拿货,招校园代理 &#xff1a;文章底部获取博主联系方式&#xff01;网上鲜花店网站&#xff08;Vue框架&#xff09;摘要 该鲜花店网站基于Vue.js框架开发&#xff0c;结合Spring、SpringM…

作者头像 李华