net分片上传和下载
public class UploadFileService { private readonly CacheService _cache; public UploadFileService(CacheService cache) { _cache = cache; } /// <summary> /// 分片上传文件 /// </summary> /// <param name="file"></param> /// <param name="chunkNumber"></param> /// <param name="totalChunks"></param> /// <param name="fileName"></param> /// <param name="filePath"></param> /// <param name="fileHash"></param> /// <returns></returns> public async Task<FileBaseInfo?> UploadFile(IFormFile file, int chunkNumber, int totalChunks, string fileName, string filePath, string fileHash) { var tempPath = Path.Combine(filePath, $"{fileHash}.part{chunkNumber}"); using (var stream = new FileStream(tempPath, FileMode.Create, FileAccess.Write)) { await file.CopyToAsync(stream); } var value = _cache.GetValue<bool[]>(fileHash); if (value is null) { value = new bool[totalChunks]; } value[chunkNumber] = true; await _cache.SetValueAsync(fileHash, value, 60 * 60); if (value.All(x => x)) { var fileBaseInfo = await MergeFileChunks(fileName, totalChunks, filePath, fileHash); await _cache.RemoveAsync(fileHash); return projectFile; } return null; } /// <summary> /// 合并文件 /// </summary> /// <param name="fileName"></param> /// <param name="totalChunks"></param> /// <param name="filePath"></param> /// <param name="fileHash"></param> /// <returns></returns> private async Task<FileBaseInfo> MergeFileChunks(string fileName, int totalChunks, string filePath, string fileHash) { var uploadPath = Path.Combine(filePath, fileName); //文件大小统计 var size = 0L; using (var destinationStream = new FileStream(uploadPath, FileMode.Create, FileAccess.Write)) { for (int i = 0; i < totalChunks; i++) { var tempPath = Path.Combine(filePath, $"{fileHash}.part{i}"); using (var sourceStream = new FileStream(tempPath, FileMode.Open, FileAccess.Read)) { await sourceStream.CopyToAsync(destinationStream); } System.IO.File.Delete(tempPath); } size = destinationStream.Length; } var fileBaseInfo = new FileBaseInfo() { FileFormat = fileName.Substring(fileName.LastIndexOf(".") + 1), FileName = fileName, FilePath = uploadPath, Size = size, }; return fileBaseInfo; } }
/// <summary> /// 分片上传文件 /// </summary> /// <param name="folderId"></param> /// <param name="file"></param> /// <param name="chunkNumber"></param> /// <param name="totalChunks"></param> /// <param name="fileName"></param> /// <param name="fileHash"></param> /// <returns></returns> [HttpPost("[action]/{folderId}")] public async Task<ActionResult> UploadFile([FromRoute] Guid folderId, IFormFile file, [FromForm] int chunkNumber, [FromForm] int totalChunks, [FromForm] string fileName, [FromForm] string fileHash) { if (file == null || chunkNumber < 0 || totalChunks <= 0 || string.IsNullOrEmpty(fileName)) { return BadRequest("无效的请求参数。"); } var folder = await _manager.FindAsync(folderId); if (folder is null) { return NotFound("不存在文件夹"); } var fileBaseInfo = await _uploadFileService.UploadFile(file, chunkNumber, totalChunks, fileName, folder.FolderPath, fileHash); if (fileBaseInfo is not null) { fileBaseInfo.FolderId = folderId; await _manager.CommandContext.AddAsync(fileBaseInfo); await _manager.SaveChangesAsync(); } return Ok(); } /// <summary> /// 查询分片上传到第几片 /// </summary> /// <param name="fileHash"></param> /// <returns></returns> [HttpGet("upload/status")] public ActionResult GetUploadStatus(string fileHash) { if (string.IsNullOrEmpty(fileHash)) { return BadRequest("文件名不能为空"); } var chunks = _cache.GetValue<bool[]>(fileHash); if (chunks is not null) { return Ok(chunks.Select((uploaded, index) => new { ChunkNumber = index, Uploaded = uploaded })); } return Ok(null); }
/// <summary> /// 下载分析输入文件夹 /// </summary> /// <param name="projectId"></param> /// <param name="folderId"></param> /// <returns></returns> [HttpGet("DownAnalysisInputDir/{projectId}/{folderId}")] public async Task<ActionResult> DownAnalysisInput([FromRoute] Guid projectId, [FromRoute] Guid folderId) { Response.Headers.ContentType = "application/zip"; await _manager.DownAnalysisInputDir(Response.BodyWriter.AsStream(), projectId, folderId); await Response.CompleteAsync(); return Ok(); }
/// <summary> /// 下载分析输入文件夹 /// </summary> /// <param name="stream"></param> /// <param name="projectId"></param> /// <param name="folderId"></param> /// <returns></returns> public async Task DownAnalysisInputDir(Stream stream, Guid projectId, Guid folderId) { using var zipArchive = new ZipArchive(stream, ZipArchiveMode.Create, true); var model = await GetAnalysisInputFolder(projectId, folderId); await AddFilesAndFoldersToZipAsync(zipArchive, projectId, model, ""); } /// <summary> /// 递归目录添加到zip中 /// </summary> /// <param name="archive"></param> /// <param name="projectId"></param> /// <param name="container"></param> /// <param name="entryPrefix"></param> /// <returns></returns> private async Task AddFilesAndFoldersToZipAsync(ZipArchive archive, Guid projectId, AnalysisInputFolderDto container, string entryPrefix) { foreach (var folder in container.Folders) { var folderEntryPrefix = entryPrefix + folder.Name + "/"; var folderEntry = archive.CreateEntry(folderEntryPrefix); var model = await GetAnalysisInputFolder(projectId, folder.Id); await AddFilesAndFoldersToZipAsync(archive, projectId, model, folderEntryPrefix); } foreach (var file in container.Files) { using var fileStream = System.IO.File.Open(file.FilePath, FileMode.Open, FileAccess.Read); using var entryStream = archive.CreateEntry(entryPrefix + file.FileName).Open(); await fileStream.CopyToAsync(entryStream); } }