GPT-Image 2隐藏玩法:输入身体数据,AI秒出专属训练图
2026/5/5 16:48:03
兄弟,作为刚入坑网络工程的山西老狗,我太懂你现在的处境了——老师要10G大文件上传的毕业设计,网上找的代码全是“断头路”,后端还没学会,前端又被兼容性卡脖子。别慌!我熬了两周啃下的原生JS+Vue3前端全栈方案,今天全盘托出,保证你能直接拿给老师演示,答辩时被夸“这届学生有点东西”!
localStorage缓存进度,关浏览器/重启电脑不丢)。/文件夹/子文件路径存储(IE8用“伪路径+元数据”方案兜底)。localhost就能跑通全流程。crypto-js实现AES加密,简历里写“熟悉国密算法”不是梦。localStorage容量优化,这些“边角料”是区分初级/中级程序员的关键。// 兼容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(加密需要) }, } };es6-promise和whatwg-fetch,否则IE8不支持Promise和fetch。File.slice,需手动引入Blob.js(https://github.com/eligrey/Blob.js),并在代码中替换file.slice为Blob.js的方法。localStorage容量限制为5MB,大文件进度需分块存储(代码中已用taskId分key存储)。localStorage缓存已上传的分片索引和大小,重启后从该位置继续上传。file.webkitRelativePath获取相对路径;IE8用随机生成的文件夹名兜底(需用户手动输入文件夹名,这里简化为随机字符串)。filePath字段创建目录结构(如/upload_123/folder_456/file.txt),确保文件层级不变。console.log打印加密前后的数据)。File API、FormData、localStorage的兼容性(https://developer.mozilla.org/)。兄弟,这套代码你拿去练手,保证答辩时老师竖大拇指!有问题直接甩日志到群里,老狗我24小时在线帮你改。记住:不会就查文档,卡壳就问群友——咱网工学子,不能输!
示例中已经包含此目录
接口地址分别对应:文件初始化,文件数据上传,文件进度,文件上传完毕,文件删除,文件夹初始化,文件夹删除,文件列表
参考:http://www.ncmem.com/doc/view.aspx?id=e1f49f3e1d4742e19135e00bd41fa3de
支持离线保存文件进度,在关闭浏览器,刷新浏览器后进行不丢失,仍然能够继续上传
支持上传文件夹并保留层级结构,同样支持进度信息离线保存,刷新页面,关闭页面,重启系统不丢失上传进度。
点击下载完整示例