一个准毕业码农的"史诗级"文件管理系统开发日记

各位码友大家好,我就是那个被10G大文件折磨得死去活来的山西大三狗!😭

血泪开发史

“老师,我这个文件管理系统能传10G文件!” —— 这话说出来我自己都不信,毕竟我的笔记本硬盘总共才256G…

前端技术选型心路历程

“原生JS+Vue3?这不是前后矛盾吗?” —— 我的导师看到我的技术栈时露出了关爱智障的眼神。但我要证明这是可以实现的!

// 大文件分片上传核心代码片段(纯手工打造,童叟无欺)
const MAX_CHUNK_SIZE = 5 * 1024 * 1024; // 5MB一片,别问为什么,问就是IE8会哭

function splitFile(file) {
  let chunks = [];
  let start = 0;
  while (start < file.size) {
    let end = Math.min(start + MAX_CHUNK_SIZE, file.size);
    chunks.push({
      chunk: file.slice(start, end),
      index: chunks.length,
      total: Math.ceil(file.size / MAX_CHUNK_SIZE),
      fileName: file.name,
      fileSize: file.size
    });
    start = end;
  }
  return chunks;
}

// 断点续传黑科技(localStorage+IndexedDB双备份)
function saveProgress(fileMd5, chunkIndex) {
  try {
    localStorage.setItem(`upload_${fileMd5}`, chunkIndex);
    // 这里应该还有IndexedDB的代码,但是...我还没学会...
  } catch (e) {
    console.log("您的存储空间已爆炸💥");
  }
}

浏览器兼容性炼狱

“IE8?Win7?国产浏览器?” —— 当我看到需求文档时,我以为回到了2010年…

// 检测浏览器是否支持File API(给IE8老爷爷准备的轮椅)
function checkBrowserSupport() {
  if (!window.File || !window.FileReader || !window.FileList || !window.Blob) {
    alert('您的浏览器太老了,建议升级到IE9...等等,IE9也很老啊!');
    return false;
  }
  return true;
}

// 文件夹上传的魔术代码(其实也没那么神奇)
function handleFolderUpload(event) {
  let files = event.target.files;
  let entries = [];
  
  // 这个webkitRelativePath是文件夹上传的关键!
  for (let i = 0; i < files.length; i++) {
    let file = files[i];
    if (file.webkitRelativePath) {
      entries.push({
        path: file.webkitRelativePath,
        file: file
      });
    }
  }
  
  return entries;
}

开发路上那些坑

  1. 断点续传的离线存储:我天真地以为localStorage就够用了,直到遇到了10G文件…现在正在恶补IndexedDB

  2. 文件夹层级保留:"webkitRelativePath"这个属性名字看起来就像是个临时工写的,但它确实是唯一能用的方案

  3. 加密传输:本来是准备用AES的,后来发现IE8不支持,现在正在研究如何用RSA+DES组合拳

求带飞环节

“有没有师傅愿意收留我这个迷途的羔羊?后端代码还是一片空白啊!Python?Java?PHP?我都可以学!” 😇

前端完整实现思路(伪代码版)

class MegaUploader {
  constructor() {
    this.chunks = [];
    this.uploaded = 0;
    this.fileMd5 = '';
    this.resumeData = this.loadResumeData();
  }
  
  // 大文件MD5计算(分片计算避免卡死)
  calculateFileMd5(file) {
    return new Promise((resolve) => {
      // 这里应该有很复杂的计算逻辑...
      setTimeout(() => resolve("mock_md5_" + file.name), 500);
    });
  }
  
  // 断点续传数据加载
  loadResumeData() {
    // 先从localStorage尝试
    // 失败后尝试IndexedDB
    // 再失败...那就从头开始吧
    return {};
  }
  
  // 真正的上传逻辑
  async upload(file) {
    this.fileMd5 = await this.calculateFileMd5(file);
    this.chunks = splitFile(file);
    
    // 检查服务器哪些分片已经上传
    let uploadedChunks = await checkServerProgress(this.fileMd5);
    
    // 只上传未完成的部分
    for (let i = 0; i < this.chunks.length; i++) {
      if (!uploadedChunks.includes(i)) {
        await this.uploadChunk(this.chunks[i]);
        saveProgress(this.fileMd5, i);
      }
    }
    
    // 所有分片完成,通知服务器合并
    await notifyServerMerge(this.fileMd5, file.name);
  }
  
  // 上传单个分片
  uploadChunk(chunk) {
    return new Promise((resolve, reject) => {
      let formData = new FormData();
      formData.append('file', chunk.chunk);
      formData.append('chunkIndex', chunk.index);
      formData.append('totalChunks', chunk.total);
      formData.append('fileMd5', this.fileMd5);
      
      // 这里应该有加密逻辑...
      fetch('/upload', {
        method: 'POST',
        body: formData
      }).then(response => {
        if (response.ok) {
          this.uploaded++;
          resolve();
        } else {
          reject('上传失败');
        }
      });
    });
  }
}

致未来的雇主大大

“虽然我现在连个完整的后端都写不出来,但我前端已经能画出漂亮的进度条了!” 💪

PS:那个QQ群是真的,红包也是真的(虽然最大那个99元的红包可能已经被我领走了…)

将组件复制到项目中

示例中已经包含此目录
image

引入组件

image

配置接口地址

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

处理事件

image

启动测试

image

启动成功

image

效果

image

数据库

image

效果预览

文件上传

文件上传

文件刷新续传

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

文件夹上传

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

下载示例

点击下载完整示例

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐