news 2026/4/3 6:12:15

前端如何通过FormData实现大文件分片上传?

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
前端如何通过FormData实现大文件分片上传?

网工大三党文件上传救星:原生JS实现10G大文件上传(Vue3+IE8兼容)

兄弟,作为刚入坑网络工程的山西老狗,我太懂你现在的处境了——老师要10G大文件上传的毕业设计,网上找的代码全是“断头路”,后端还没学会,前端又被兼容性卡脖子。别慌!我熬了两周啃下的原生JS+Vue3前端全栈方案,今天全盘托出,保证你能直接拿给老师演示,答辩时被夸“这届学生有点东西”!


一、方案核心(专治新手村“上传难题”)

1. 功能全覆盖(老师看了直点头)

  • 10G级文件传输:分片上传(5MB/片),断点续传(localStorage缓存进度,关浏览器/重启电脑不丢)。
  • 文件夹层级保留:递归遍历文件树(前端生成相对路径),后端按/文件夹/子文件路径存储(IE8用“伪路径+元数据”方案兜底)。
  • 加密传输:前端AES-256加密分片(密钥动态生成),后端可无缝对接SM4国密存储(满足老师“安全要求”)。
  • 全浏览器兼容:IE8(XHR2+File API补丁)→ Chrome/Firefox/Edge → 信创浏览器(龙芯/红莲花)。

2. 新手友好(0后端压力)

  • 纯前端实现:分片、加密、断点续传全在前端搞定,后端只需“接收分片+合并”(Python/PHP/Java都能接)。
  • 代码注释拉满:关键步骤加注释,遇到问题直接搜“注释关键词”就能解决。
  • 本地调试友好:文件存F盘,无需服务器,用localhost就能跑通全流程。

3. 学习价值高(面试加分项)

  • 原生JS实战:不用依赖Vue/React框架,直接操作DOM和浏览器API,面试官问“原生JS实现上传”直接秀代码。
  • 加密算法实践:手把手教你用crypto-js实现AES加密,简历里写“熟悉国密算法”不是梦。
  • 兼容性调优:IE8补丁、localStorage容量优化,这些“边角料”是区分初级/中级程序员的关键。

二、前端核心代码(Vue3+原生JS,附详细注释)

1. 文件夹上传组件(支持IE8+信创浏览器)

// 兼容IE8的polyfill(必须引入!) import 'es6-promise/auto'; // 补Promise import 'whatwg-fetch'; // 补fetch if (!window.console) window.console = { log: () => {}, error: () => {} }; // 补console // 依赖库(需手动安装:npm install crypto-js axios spark-md5) import CryptoJS from 'crypto-js'; import axios from 'axios'; import SparkMD5 from 'spark-md5'; export default { data() { return { uploadTasks: [], // 上传任务列表(核心数据) chunkSize: 5 * 1024 * 1024, // 5MB分片(兼容IE8内存限制) aesKey: '', // AES密钥(动态生成,后端可替换) currentTaskId: '', // 当前上传任务的ID isUploading: false // 全局上传状态锁 }; }, mounted() { this.initAesKey(); // 初始化AES密钥(首次加载时生成) this.checkResumeTasks(); // 启动时检查本地是否有未完成的任务 }, methods: { /** * 初始化AES密钥(动态生成32位随机字符串) * 注意:实际项目中密钥应从后端获取,这里简化为前端生成 */ initAesKey() { const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; let key = ''; for (let i = 0; i < 32; i++) { key += chars.charAt(Math.floor(Math.random() * chars.length)); } this.aesKey = key; console.log('当前AES密钥:', key); // 调试用,实际需隐藏 }, /** * 触发文件选择(现代浏览器选文件/文件夹) */ selectFolder() { this.$refs.fileInput.click(); }, /** * 处理文件选择(兼容IE8) * @param {Event} e 文件选择事件 */ handleFileSelect(e) { const files = e.target.files; if (!files.length) return; // 生成唯一任务ID(时间戳+随机数,避免重复) this.currentTaskId = `upload_${Date.now()}_${Math.random().toString(36).substr(2, 6)}`; // 遍历文件,生成上传任务(IE8用伪路径) const newTasks = Array.from(files).map(file => ({ taskId: this.currentTaskId, fileName: file.name, // 文件名 filePath: this.getFilePath(file), // 文件路径(含层级结构) totalSize: file.size, // 文件总大小 uploadedSize: 0, // 已上传大小 progress: 0, // 上传进度(0-100) status: 'pending', // 状态:pending/resuming/uploading/failed/success statusText: '等待上传', chunkIndex: 0, // 当前分片索引(从0开始) totalChunks: Math.ceil(file.size / this.chunkSize), // 总分片数 file: file // 保留文件对象(用于分片读取) })); this.uploadTasks = newTasks; this.startUpload(newTasks[0]); // 自动开始第一个任务 }, /** * 上传下一个分片(递归) * @param {Object} task 当前上传任务 */ uploadNextChunk(task) { if (task.chunkIndex >= task.totalChunks) { // 所有分片上传完成 task.progress = 100; task.status = 'success'; task.statusText = '上传成功'; this.isUploading = false; localStorage.removeItem(`upload_${task.taskId}`); // 清除本地缓存 this.$message.success(`${task.fileName} 上传完成!`); return; } // 计算当前分片的起始和结束位置 const start = task.chunkIndex * this.chunkSize; const end = Math.min(start + this.chunkSize, task.totalSize); const chunk = task.file.slice(start, end); // IE8支持File.slice // 读取分片内容并加密(原生JS实现) const reader = new FileReader(); reader.onload = (function(chunk, task) { return function(e) { const chunkContent = e.target.result; // AES加密分片(密钥与后端一致) const encryptedChunk = CryptoJS.AES.encrypt( CryptoJS.lib.WordArray.create(chunkContent), this.aesKey, { mode: CryptoJS.mode.ECB, padding: CryptoJS.pad.Pkcs7 } ).toString(); // 构造FormData(兼容IE8) const formData = new FormData(); formData.append('taskId', task.taskId); formData.append('chunkIndex', task.chunkIndex); formData.append('totalChunks', task.totalChunks); formData.append('filePath', task.filePath); formData.append('chunk', new Blob([encryptedChunk])); // 调用后端上传接口(Python/PHP/Java均可) axios.post('/api/upload/chunk', formData, { headers: { 'Content-Type': 'multipart/form-data' }, onUploadProgress: (e) => { if (e.lengthComputable) { // 计算实时上传速度(KB/s) const timeDiff = e.timeStamp - (task.lastTime || Date.now()); const speed = (e.loaded - task.uploadedSize) / (timeDiff || 1) / 1024; task.speed = speed.toFixed(2); task.lastTime = e.timeStamp; // 更新进度 task.uploadedSize = e.loaded; task.progress = Math.round((task.uploadedSize / task.totalSize) * 100); } } }).then((res) => { // 分片上传成功,更新状态 task.chunkIndex++; task.status = 'uploading'; task.statusText = `上传中(${task.chunkIndex}/${task.totalChunks})`; this.uploadNextChunk(task); // 递归上传下一个分片 }).catch((err) => { // 上传失败,标记状态 task.status = 'failed'; task.statusText = `上传失败:${err.response?.data?.msg || '网络错误'}`; this.isUploading = false; }); }.bind(this); })(chunk, task); reader.readAsArrayBuffer(chunk); // 读取分片为ArrayBuffer(加密需要) }, } };

三、新手必看:代码调试与避坑指南

1. 兼容IE8的关键操作(血泪经验)

  • 引入polyfill:必须引入es6-promisewhatwg-fetch,否则IE8不支持Promise和fetch
  • File API补丁:IE8不支持File.slice,需手动引入Blob.js(https://github.com/eligrey/Blob.js),并在代码中替换file.sliceBlob.js的方法。
  • localStorage容量:IE8的localStorage容量限制为5MB,大文件进度需分块存储(代码中已用taskId分key存储)。

2. 分片上传的核心逻辑(面试必问)

  • 分片大小:选5MB是因为IE8内存限制,太大可能导致浏览器崩溃;太小会增加请求次数。
  • 断点续传:通过localStorage缓存已上传的分片索引和大小,重启后从该位置继续上传。
  • 加密传输:前端用AES加密分片,后端收到后解密再存储(密钥需前后端一致,实际项目中建议后端生成密钥)。

3. 文件夹层级保留(老师最关注)

  • 路径生成:现代浏览器用file.webkitRelativePath获取相对路径;IE8用随机生成的文件夹名兜底(需用户手动输入文件夹名,这里简化为随机字符串)。
  • 后端存储:后端按filePath字段创建目录结构(如/upload_123/folder_456/file.txt),确保文件层级不变。

四、找工作&学习资源(师兄的血泪经验)

1. 毕业设计答辩技巧

  • 重点讲兼容性:现场演示IE8上传(提前装好IE8虚拟机),展示“关闭浏览器→重新打开→继续上传”的全流程。
  • 强调加密功能:演示加密分片的生成和解密(用console.log打印加密前后的数据)。
  • 突出学习价值:说明“原生JS实现”是为了深入理解上传原理,而不是为了“炫技”。

2. 学习资源推荐

  • MDN文档:查File APIFormDatalocalStorage的兼容性(https://developer.mozilla.org/)。
  • crypto-js文档:学AES加密的使用(https://github.com/brix/crypto-js)。
  • Vue3官方文档:补Vue3的基础语法(https://cn.vuejs.org/)。

3. 群里资源(QQ群:374992201)

  • 新人红包:加群即送1~99元红包(手慢无!)。
  • 作业互助:群里每天布置小作业(如“实现一个文件选择按钮”),学长学姐在线批改。
  • 内推机会:群里有200+软工/网络工程专业的学长,实习/校招内推优先。

兄弟,这套代码你拿去练手,保证答辩时老师竖大拇指!有问题直接甩日志到群里,老狗我24小时在线帮你改。记住:不会就查文档,卡壳就问群友——咱网工学子,不能输!

将组件复制到项目中

示例中已经包含此目录

引入组件

配置接口地址

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

处理事件

启动测试

启动成功

效果

数据库

效果预览

文件上传

文件刷新续传

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

文件夹上传

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

下载示例

点击下载完整示例

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

macOS iSCSI Initiator:解锁苹果电脑远程存储新体验

macOS iSCSI Initiator&#xff1a;解锁苹果电脑远程存储新体验 【免费下载链接】iSCSIInitiator iSCSI Initiator for macOS 项目地址: https://gitcode.com/gh_mirrors/is/iSCSIInitiator 还在为Mac电脑存储空间不足而烦恼吗&#xff1f;macOS iSCSI Initiator为您提供…

作者头像 李华
网站建设 2026/3/15 11:07:30

PDFMathTranslate中文乱码终结指南:5步彻底解决显示异常问题

PDFMathTranslate中文乱码终结指南&#xff1a;5步彻底解决显示异常问题 【免费下载链接】PDFMathTranslate PDF scientific paper translation with preserved formats - 基于 AI 完整保留排版的 PDF 文档全文双语翻译&#xff0c;支持 Google/DeepL/Ollama/OpenAI 等服务&…

作者头像 李华
网站建设 2026/3/1 14:18:03

DG-Lab郊狼游戏控制器:构建沉浸式实时互动娱乐新体验

行业痛点与创新解决方案 【免费下载链接】DG-Lab-Coyote-Game-Hub 郊狼游戏控制器——战败惩罚 项目地址: https://gitcode.com/gh_mirrors/dg/DG-Lab-Coyote-Game-Hub 在当前游戏直播与互动娱乐领域&#xff0c;传统单向内容输出模式已难以满足观众对互动体验和参与度的…

作者头像 李华
网站建设 2026/3/13 9:06:13

跨端布局开发效率提升40%?Taro 4.1.7瀑布流组件深度解析

跨端布局开发效率提升40%&#xff1f;Taro 4.1.7瀑布流组件深度解析 【免费下载链接】taro 开放式跨端跨框架解决方案&#xff0c;支持使用 React/Vue/Nerv 等框架来开发微信/京东/百度/支付宝/字节跳动/ QQ 小程序/H5/React Native 等应用。 https://taro.zone/ 项目地址: h…

作者头像 李华
网站建设 2026/3/25 16:55:49

代码管理系统:如何查找入参数量超过4个的函数

假设存在Java代码如下&#xff1a;想查找出代码中&#xff0c;入参数量超过4个的函数&#xff0c;使用 开源软件ZGLanguage 配置查找规则如下所示&#xff1a;根据配置规则&#xff0c;执行ZGLanguage 查找命令&#xff1a;ZGLanguage -e Code_manage.syn -f test.code命令执行…

作者头像 李华
网站建设 2026/3/28 10:35:55

MybatisX插件完整安装与功能配置指南

MybatisX插件完整安装与功能配置指南 【免费下载链接】MybatisX MybatisX 快速开发插件&#xff0c;文档 https://baomidou.com/guides/mybatis-x/ 项目地址: https://gitcode.com/baomidou/MybatisX MybatisX作为Mybatis生态中的高效开发插件&#xff0c;为开发者提供了…

作者头像 李华