• 网络不稳定:在网络中断的情况下,用户需要重新上传整个文件。
• 流式上传:通过流式处理避免将整个文件加载到内存中。
public void ConfigureServices(IServiceCollection services) { services.Configure<IISServerOptions>(options => { options.MaxRequestBodySize = int.MaxValue; // 设置最大请求大小 }); }流式上传
[HttpPost("upload")] public async Task<IActionResult> UploadLargeFile(IFormFile file) { if (file == null || file.Length == 0) return BadRequest("未选择文件"); // 堆代码 duidaima.com var filePath = Path.Combine(Directory.GetCurrentDirectory(), "uploads", file.FileName); using (var stream = new FileStream(filePath, FileMode.Create)) { await file.CopyToAsync(stream); // 使用流式处理 } return Ok("文件上传成功"); }二、分块上传
• 后端合并:接收所有小块后,按顺序合并为完整文件。
function uploadFile(file) { const chunkSize = 1 * 1024 * 1024; // 每块1MB let start = 0; let end = chunkSize; functionuploadChunk() { if (start >= file.size) return; const chunk = file.slice(start, end); const formData = newFormData(); formData.append('chunk', chunk); formData.append('fileName', file.name); fetch('/api/upload/chunk', { method: 'POST', body: formData }).then(() => { start = end; end += chunkSize; uploadChunk(); // 递归上传下一小块 }); } uploadChunk(); }后端合并
private staticreadonly Dictionary<string, List<byte[]>> chunks = new(); [HttpPost("chunk")] public IActionResult UploadChunk([FromForm] byte[] chunk, [FromForm] string fileName) { if (!chunks.ContainsKey(fileName)) chunks[fileName] = new List<byte[]>(); chunks[fileName].Add(chunk); return Ok("分块上传成功"); } [HttpPost("merge")] public IActionResult MergeChunks([FromForm] string fileName) { if (!chunks.ContainsKey(fileName)) return NotFound("分块不存在"); var allBytes = chunks[fileName].SelectMany(c => c).ToArray(); var filePath = Path.Combine(Directory.GetCurrentDirectory(), "uploads", fileName); System.IO.File.WriteAllBytes(filePath, allBytes); chunks.Remove(fileName); return Ok("文件合并成功"); }三、断点续传
public class UploadProgress { public string FileName { get; set; } public int TotalChunks { get; set; } public List<bool> UploadedChunks { get; set; } = new(); }恢复上传逻辑
[HttpGet("progress/{fileName}")] public IActionResult GetUploadProgress(string fileName) { // 查询数据库或文件系统获取上传进度 var progress = GetProgressFromDatabase(fileName); return Ok(progress); }四、高效的文件下载
[HttpGet("download/{fileName}")] public IActionResult DownloadFile(string fileName) { var filePath = Path.Combine(Directory.GetCurrentDirectory(), "uploads", fileName); if (!System.IO.File.Exists(filePath)) return NotFound("文件不存在"); var stream = new FileStream(filePath, FileMode.Open, FileAccess.Read); return File(stream, "application/octet-stream", fileName); }支持断点续传
[HttpGet("resume-download/{fileName}")] public IActionResult ResumeDownload(string fileName) { var filePath = Path.Combine(Directory.GetCurrentDirectory(), "uploads", fileName); if (!System.IO.File.Exists(filePath)) return NotFound("文件不存在"); var fileSize = new FileInfo(filePath).Length; var rangeHeader = Request.Headers["Range"].ToString(); if (!string.IsNullOrEmpty(rangeHeader)) { var rangeValues = RangeHeaderValue.Parse(rangeHeader).Ranges.First(); var start = rangeValues.From ?? 0; var length = rangeValues.To - start + 1; var stream = new FileStream(filePath, FileMode.Open, FileAccess.Read); stream.Seek(start, SeekOrigin.Begin); Response.Headers["Content-Range"] = $"bytes {start}-{rangeValues.To}/{fileSize}"; Response.StatusCode = StatusCodes.Status206PartialContent; return File(stream, "application/octet-stream", fileName, false, (int)length); } return File(new FileStream(filePath, FileMode.Open, FileAccess.Read), "application/octet-stream", fileName); }总结