网工大三党文件上传救星:原生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-promise和whatwg-fetch,否则IE8不支持Promise和fetch。
  • File API补丁:IE8不支持File.slice,需手动引入Blob.js(https://github.com/eligrey/Blob.js),并在代码中替换file.slice为Blob.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 API、FormData、localStorage的兼容性(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小时在线帮你改。记住:不会就查文档,卡壳就问群友——咱网工学子,不能输!

将组件复制到项目中

示例中已经包含此目录
image

引入组件

image

配置接口地址

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

处理事件

image

启动测试

image

启动成功

image

效果

image

数据库

image

效果预览

文件上传

文件上传

文件刷新续传

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

文件夹上传

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

下载示例

点击下载完整示例

Logo

火山引擎视频云技术社区,是面向 AI 音视频开发者的技术交流平台。这里汇聚源自抖音、豆包等亿级 DAU 产品的 RTC、直播、点播、AI 媒体处理、音视频互动技术,提供接入指南、最佳实践、性能调优、场景案例、Demo 代码、开源项目、白皮书和 API 文档。社区汇聚官方工程师与一线开发者,为 AI 视频通话、数字人、AI 视频处理等应用的开发与落地提供技术支持。

更多推荐