作为一名长期奋战在.NET外包一线的开发者,我深知大文件上传功能在实际项目中的痛点。最近接到的这个需求颇具代表性:客户要求实现20GB以上大文件上传功能,同时必须兼容IE8浏览器,预算却只有区区几百元。这种"既要又要还要"的需求在外包项目中屡见不鲜,但经过三个月的实战打磨,我总结出了一套高性价比的解决方案。
这套方案的核心价值在于:
系统采用经典的三层架构,但在各层都针对大文件传输做了特殊优化:
code复制前端(浏览器) → 中间层(ASP.NET) → 存储层(文件系统+SQL Server)
文件分片是大文件上传的核心技术,我们的方案采用以下策略:
分片大小:固定5MB/片
分片命名规则:
code复制/上传任务ID/分片序号.dat
例如:/upload_123456/3.dat
分片元数据管理:
断点续传是本方案的一大亮点,其实现基于以下机制:
前端持久化:
后端状态跟踪:
sql复制CREATE TABLE UploadProgress (
TaskId NVARCHAR(255) NOT NULL,
ChunkIndex INT NOT NULL,
TotalChunks INT NOT NULL,
FilePath NVARCHAR(1000) NOT NULL,
UploadedSize BIGINT NOT NULL,
Status NVARCHAR(50) NOT NULL
);
恢复流程:
现代浏览器与IE8采用不同的文件选择策略:
javascript复制// 现代浏览器(支持目录上传)
function selectFolder() {
inputElement.webkitdirectory = true;
inputElement.click();
}
// IE8兼容方案
function handleFileSelect(e) {
const files = e.target.files;
if(!files.length) return;
// IE8下使用文件名模拟路径
const filePath = file.webkitRelativePath || file.name;
}
分片上传是整个系统的核心,其实现流程如下:
初始化上传任务:
javascript复制const task = {
taskId: `upload_${Date.now()}`,
fileName: file.name,
filePath: `/folder_${taskId}/${filePath}`,
totalSize: file.size,
chunkSize: 5 * 1024 * 1024, // 5MB
chunkIndex: 0,
totalChunks: Math.ceil(file.size / chunkSize)
};
分片读取与加密:
javascript复制const reader = new FileReader();
reader.onload = function(e) {
const encrypted = CryptoJS.AES.encrypt(
CryptoJS.lib.WordArray.create(e.target.result),
aesKey,
{ mode: CryptoJS.mode.ECB }
).toString();
// 上传加密后的分片
};
reader.readAsArrayBuffer(chunk);
进度跟踪:
javascript复制xhr.upload.addEventListener('progress', function(e) {
const speed = (e.loaded - task.uploadedSize) /
(e.timeStamp - (task.lastTime || Date.now())) / 1024;
task.speed = speed.toFixed(2);
task.progress = Math.round((e.loaded / task.totalSize) * 100);
});
针对IE8的特殊处理:
XHR2 polyfill:
File API兼容:
javascript复制// IE8下使用伪路径
filePath = file.name;
// 分片使用slice方法
chunk = file.slice(start, end);
表单提交:
ASP.NET后端处理分片上传的核心逻辑:
csharp复制public void UploadChunk(string taskId, int chunkIndex, HttpPostedFile chunk)
{
// 1. 解密分片
byte[] decrypted = AesDecrypt(ReadFully(chunk.InputStream), GetAesKey());
// 2. 保存分片
string savePath = Path.Combine(_storagePath, taskId, chunkIndex.ToString());
Directory.CreateDirectory(Path.GetDirectoryName(savePath));
File.WriteAllBytes(savePath, decrypted);
// 3. 记录进度
SaveProgressToDb(taskId, chunkIndex, chunk.ContentLength);
}
解密算法实现:
csharp复制private byte[] AesDecrypt(byte[] data, string key)
{
using (var aes = Aes.Create())
{
aes.Key = Encoding.UTF8.GetBytes(key);
aes.Mode = CipherMode.ECB;
aes.Padding = PaddingMode.PKCS7;
using (var decryptor = aes.CreateDecryptor())
{
return decryptor.TransformFinalBlock(data, 0, data.Length);
}
}
}
当所有分片上传完成后,后端需要合并分片:
csharp复制public void MergeChunks(string taskId, string filePath)
{
var chunks = GetChunksFromDb(taskId).OrderBy(c => c.ChunkIndex);
using (var fs = new FileStream(finalPath, FileMode.Create))
{
foreach (var chunk in chunks)
{
var chunkData = File.ReadAllBytes(GetChunkPath(taskId, chunk.ChunkIndex));
fs.Write(chunkData, 0, chunkData.Length);
File.Delete(GetChunkPath(taskId, chunk.ChunkIndex));
}
}
}
针对大文件上传场景优化的数据库设计:
sql复制CREATE TABLE UploadProgress (
Id INT IDENTITY PRIMARY KEY,
TaskId NVARCHAR(255) NOT NULL,
ChunkIndex INT NOT NULL,
TotalChunks INT NOT NULL,
FilePath NVARCHAR(1000) NOT NULL,
UploadedSize BIGINT NOT NULL,
Status NVARCHAR(50) NOT NULL,
CreateTime DATETIME DEFAULT GETDATE(),
UNIQUE (TaskId, ChunkIndex)
);
CREATE INDEX IX_TaskId ON UploadProgress(TaskId);
为确保大文件上传稳定运行,需要调整以下IIS设置:
web.config配置:
xml复制<system.web>
<httpRuntime maxRequestLength="2147483647" />
</system.web>
<system.webServer>
<security>
<requestFiltering>
<requestLimits maxAllowedContentLength="2147483647" />
</requestFiltering>
</security>
</system.webServer>
应用程序池设置:
文件存储策略:
数据库优化:
完善的监控体系有助于快速定位问题:
前端日志:
javascript复制function logError(message) {
console.error(message);
// 可扩展为发送到日志服务器
}
后端日志:
csharp复制try {
// 上传逻辑
} catch (Exception ex) {
Logger.Error($"上传失败:{ex.Message}", ex);
throw;
}
现象:上传过程中频繁中断
排查步骤:
解决方案:
csharp复制// 增加超时时间
HttpContext.Current.Server.ScriptTimeout = 3600; // 1小时
常见问题:
解决方案:
javascript复制function checkMemory() {
try {
var arr = new Array(1000000);
return true;
} catch(e) {
return false;
}
}
并行上传:
压缩传输:
javascript复制// 前端压缩
const compressed = pako.deflate(chunkData);
智能分片:
前端加密:
HTTPS强制:
csharp复制if(!Request.IsSecureConnection) {
Response.Redirect("https://" + Request.Url.Host + Request.Url.PathAndQuery);
}
文件加密存储:
csharp复制public void SaveFile(string path, byte[] data)
{
var encrypted = SM4Encrypt(data, storageKey);
File.WriteAllBytes(path, encrypted);
}
访问控制:
文件类型检查:
csharp复制bool IsAllowedExtension(string filename)
{
var ext = Path.GetExtension(filename).ToLower();
var allowed = new[] { ".pdf", ".doc", ".jpg" };
return allowed.Contains(ext);
}
病毒扫描:
对于更大规模的应用,可以考虑:
分布式部署:
云存储集成:
csharp复制public void SaveToCloud(byte[] data, string path)
{
var client = new AmazonS3Client();
client.PutObject(new PutObjectRequest {
BucketName = "my-bucket",
Key = path,
InputStream = new MemoryStream(data)
});
}
针对移动设备的优化:
响应式界面:
css复制@media (max-width: 768px) {
.upload-panel {
width: 100%;
}
}
后台上传:
将系统拆分为微服务:
上传服务:
文件管理服务:
通知服务:
这套大文件上传方案经过多个实际项目验证,能够稳定处理TB级文件传输,特别适合预算有限但要求严格的外包项目。核心代码已经过优化,可直接用于生产环境,开发者只需根据具体需求调整配置参数即可快速部署。