2023年10月15日 - 大文件上传系统开发日志

项目背景

今天正式接手了这个具有挑战性的文件上传系统项目。客户需求相当专业:需要支持20G大文件传输、文件夹层级结构保持、断点续传,还要兼容IE8这样的古董浏览器。作为个人开发者,这绝对是一个证明自己技术实力的好机会。

技术选型分析

经过一天的调研和验证,我确定了以下技术方案:

前端方案

  • 对于现代浏览器:使用HTML5 File API + IndexedDB实现
  • 对于IE8:使用Flash作为后备方案(虽然不情愿,但不得不支持)
  • 加密方案:使用CryptoJS实现SM4和AES加密
  • 进度管理:LocalStorage + Service Worker双保险

后端方案

  • 文件切片:10MB为一个chunk
  • 断点续传:SQL Server记录上传状态
  • 存储策略:阿里云OSS分片上传API
  • 加密存储:服务器端二次加密

核心代码片段

前端关键代码 (基于原生JS)

// 文件上传核心逻辑
class BigFileUploader {
  constructor() {
    this.chunkSize = 10 * 1024 * 1024; // 10MB
    this.maxRetry = 3;
    this.concurrentUploads = 3;
  }

  // 处理文件夹上传
  async uploadFolder(folder, basePath = '') {
    const entries = await this.readDirectory(folder);
    for (let entry of entries) {
      if (entry.isDirectory) {
        await this.uploadFolder(entry, `${basePath}/${entry.name}`);
      } else {
        await this.uploadFile(entry.file, basePath);
      }
    }
  }

  // 文件分片上传
  async uploadFile(file, relativePath) {
    const totalChunks = Math.ceil(file.size / this.chunkSize);
    const fileId = this.generateFileId(file, relativePath);
    
    // 检查已上传的分片
    const uploadedChunks = await this.checkUploadedChunks(fileId);
    
    for (let chunkIndex = 0; chunkIndex < totalChunks; chunkIndex++) {
      if (uploadedChunks.includes(chunkIndex)) continue;
      
      const chunk = file.slice(
        chunkIndex * this.chunkSize,
        Math.min(file.size, (chunkIndex + 1) * this.chunkSize)
      );
      
      const encryptedChunk = this.encryptChunk(chunk);
      await this.uploadChunk(fileId, chunkIndex, encryptedChunk);
      
      // 保存进度
      this.saveProgress(fileId, chunkIndex);
    }
    
    // 通知服务器完成上传
    await this.completeUpload(fileId, totalChunks);
  }
}

后端关键代码 (C# WebForm)

// 文件上传处理类
public class FileUploadHandler : IHttpHandler
{
    public void ProcessRequest(HttpContext context)
    {
        string action = context.Request["action"];
        
        switch (action)
        {
            case "upload":
                HandleChunkUpload(context);
                break;
            case "check":
                CheckChunkStatus(context);
                break;
            case "complete":
                CompleteFileUpload(context);
                break;
        }
    }

    private void HandleChunkUpload(HttpContext context)
    {
        string fileId = context.Request["fileId"];
        int chunkIndex = int.Parse(context.Request["chunkIndex"]);
        
        // 获取加密分片数据
        Stream chunkStream = context.Request.InputStream;
        
        // 解密数据
        byte[] decryptedData = DecryptChunk(chunkStream, "SM4");
        
        // 存储到临时位置
        string tempPath = GetChunkTempPath(fileId, chunkIndex);
        File.WriteAllBytes(tempPath, decryptedData);
        
        // 记录数据库
        RecordChunkUpload(fileId, chunkIndex, tempPath);
    }
    
    private void CompleteFileUpload(HttpContext context)
    {
        string fileId = context.Request["fileId"];
        int totalChunks = int.Parse(context.Request["totalChunks"]);
        
        // 验证所有分片是否完整
        if (!VerifyAllChunks(fileId, totalChunks))
        {
            context.Response.Write("{\"status\":\"error\", \"message\":\"Missing chunks\"}");
            return;
        }
        
        // 合并分片
        string finalPath = MergeChunks(fileId, totalChunks);
        
        // 上传到阿里云OSS
        UploadToOSS(finalPath);
        
        // 清理临时文件
        CleanTempFiles(fileId);
        
        context.Response.Write("{\"status\":\"success\"}");
    }
}

今日技术难点与解决方案

  1. IE8兼容性问题

    • 方案:使用Flash作为后备方案,检测浏览器支持情况自动切换
    • 代码:通过条件注释加载不同版本的JS文件
  2. 大文件夹遍历性能

    • 问题:1000+文件的文件夹会导致UI冻结
    • 方案:使用Web Workers进行后台处理
    • 实现:将文件遍历逻辑放入Worker中
  3. 断点续传可靠性

    • 问题:浏览器关闭后如何恢复
    • 方案:LocalStorage + Service Worker双备份
    • 优化:定期将进度同步到服务器

明日计划

  1. 完成文件夹下载功能的非打包实现
  2. 测试各浏览器下的加密传输性能
  3. 编写SQL Server的存储过程优化分片查询
  4. 开始编写技术文档

项目感悟

这个项目确实很有挑战,但也是提升全栈能力的好机会。特别是对老浏览器的兼容,让我重新认识了渐进增强的重要性。希望最终交付的产品能让客户满意,也为自己积累一个重量级的案例。

PS:欢迎同行交流,我的QQ群:374992201,大家可以一起讨论技术难题,共享外包资源。


注:以上代码为关键部分示例,完整实现需要考虑更多边界条件和错误处理。实际开发中建议使用TypeScript增强代码可靠性。

设置框架

安装.NET Framework 4.7.2
https://dotnet.microsoft.com/en-us/download/dotnet-framework/net472
框架选择4.7.2
Alt

添加3rd引用

Alt

编译项目

Alt

NOSQL

NOSQL无需任何配置可直接访问页面进行测试
Alt

SQL

使用IIS
大文件上传测试推荐使用IIS以获取更高性能。
Alt

使用IIS Express

小文件上传测试可以使用IIS Express
Alt

创建数据库

Alt

配置数据库连接信息

Alt

检查数据库配置

Alt

访问页面进行测试

Alt
相关参考:
文件保存位置

效果预览

文件上传

文件上传

文件刷新续传

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

文件夹上传

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

下载完整示例

下载完整示例

Logo

这里是“一人公司”的成长家园。我们提供从产品曝光、技术变现到法律财税的全栈内容,并连接云服务、办公空间等稀缺资源,助你专注创造,无忧运营。

更多推荐