jQuery下如何实现文件的分片上传功能?
今天正式接手了这个具有挑战性的文件上传系统项目。客户需求相当专业:需要支持20G大文件传输、文件夹层级结构保持、断点续传,还要兼容IE8这样的古董浏览器。作为个人开发者,这绝对是一个证明自己技术实力的好机会。
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\"}");
}
}
今日技术难点与解决方案
-
IE8兼容性问题:
- 方案:使用Flash作为后备方案,检测浏览器支持情况自动切换
- 代码:通过条件注释加载不同版本的JS文件
-
大文件夹遍历性能:
- 问题:1000+文件的文件夹会导致UI冻结
- 方案:使用Web Workers进行后台处理
- 实现:将文件遍历逻辑放入Worker中
-
断点续传可靠性:
- 问题:浏览器关闭后如何恢复
- 方案:LocalStorage + Service Worker双备份
- 优化:定期将进度同步到服务器
明日计划
- 完成文件夹下载功能的非打包实现
- 测试各浏览器下的加密传输性能
- 编写SQL Server的存储过程优化分片查询
- 开始编写技术文档
项目感悟
这个项目确实很有挑战,但也是提升全栈能力的好机会。特别是对老浏览器的兼容,让我重新认识了渐进增强的重要性。希望最终交付的产品能让客户满意,也为自己积累一个重量级的案例。
PS:欢迎同行交流,我的QQ群:374992201,大家可以一起讨论技术难题,共享外包资源。
注:以上代码为关键部分示例,完整实现需要考虑更多边界条件和错误处理。实际开发中建议使用TypeScript增强代码可靠性。
设置框架
安装.NET Framework 4.7.2
https://dotnet.microsoft.com/en-us/download/dotnet-framework/net472
框架选择4.7.2
添加3rd引用

编译项目

NOSQL
NOSQL无需任何配置可直接访问页面进行测试
SQL
使用IIS
大文件上传测试推荐使用IIS以获取更高性能。
使用IIS Express
小文件上传测试可以使用IIS Express
创建数据库

配置数据库连接信息

检查数据库配置

访问页面进行测试

相关参考:
文件保存位置,
效果预览
文件上传

文件刷新续传
支持离线保存文件进度,在关闭浏览器,刷新浏览器后进行不丢失,仍然能够继续上传
文件夹上传
支持上传文件夹并保留层级结构,同样支持进度信息离线保存,刷新页面,关闭页面,重启系统不丢失上传进度。
下载完整示例
更多推荐



所有评论(0)