后端控制器:
//用于保存的文件夹 static readonly string uploadFolder = "UploadFolder"; //目录分隔符,兼容不同系统 static readonly char dirSeparator = Path.DirectorySeparatorChar; string GetTmpChunkDir(string fileName) => HttpContext.Session.TryGetValue(fileName, out byte[] bytes) ? Encoding.Default.GetString(bytes) : ""; //[UnifyResult(typeof(JsonResult))] //保存文件 [HttpPost] public async Task<JsonResult> SaveChunkFile(IFormFile chunk, string fileName, int chunkIndex, int chunkCount) { try { if (chunk.Length == 0) { return Json(new { success = false, msg = "File Length 0", }); } if (chunkIndex == 0) { //第一次上传时,生成一个随机id,做为保存块的临时文件夹,记录到session HttpContext.Session.Set(fileName, Encoding.Default.GetBytes(Guid.NewGuid().ToString("N"))); } if (!Directory.Exists(uploadFolder)) Directory.CreateDirectory(uploadFolder); var fullChunkDir = uploadFolder + dirSeparator + GetTmpChunkDir(fileName); if (!Directory.Exists(fullChunkDir)) Directory.CreateDirectory(fullChunkDir); var blob = chunk.FileName; var newFileName = blob + chunkIndex + Path.GetExtension(fileName); var filePath = fullChunkDir + Path.DirectorySeparatorChar + newFileName; //保存文件块 using (var stream = new FileStream(filePath, FileMode.Create)) { await chunk.CopyToAsync(stream); } //所有块上传完成 if (chunkIndex == chunkCount - 1) { //也可以在这合并,在这合并就不用ajax调用CombineChunkFile合并 //CombineChunkFile(fileName); } var obj = new { success = true, date = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"), newFileName, originalFileName = fileName, size = chunk.Length, nextIndex = chunkIndex + 1, }; return Json(obj); } catch (Exception ex) { return Json(new { success = false, msg = ex.Message, }); } } [UnifyResult(typeof(JsonResult))] //合并文件 public async Task<JsonResult> CombineChunkFile(string fileName) { try { return await Task.Run(() => { var tmpDir = GetTmpChunkDir(fileName); var fullChunkDir = uploadFolder + dirSeparator + tmpDir; var beginTime = DateTime.Now; var newFileName = tmpDir + Path.GetExtension(fileName); var destFile = uploadFolder + dirSeparator + newFileName; //获取临时文件夹内的所有文件块,排好序 var files = Directory.GetFiles(fullChunkDir).OrderBy(x => x.Length).ThenBy(x => x).ToList(); using (var destStream = System.IO.File.OpenWrite(destFile)) { files.ForEach(chunk => { using (var chunkStream = System.IO.File.OpenRead(chunk)) { chunkStream.CopyTo(destStream); } System.IO.File.Delete(chunk); }); Directory.Delete(fullChunkDir); } var totalTime = DateTime.Now.Subtract(beginTime).TotalSeconds; return Json(new { success = true, destFile = destFile.Replace('\\', '/'), msg = $"combine completed ! {totalTime} s", }); }); } catch (Exception ex) { return Json(new { success = false, msg = ex.Message, }); } finally { HttpContext.Session.Remove(fileName); } }
本人用的百小僧MVC框架
前端视图代码:
@{ ViewData["Title"] = "Home Page"; } <div class="text-center"> <div> <input type="file" id="file1" value="" /> <input type="button" id="btnUplaod" value="Upload" multiple="multiple" /> </div> <div id="completedChunks"></div> <div id="percent">0%</div> <div id="progress" style="width:200px;height:10px;background:linear-gradient(45deg, #ff0084 0%, #e8c5d7 0%);"></div> @section Scripts{ <script src="~/lib/vue/axios.min.js"></script> <script> //important //slice()函数要IE10+才支持。 //Firefox 12及更早版本的使用mozSlice() //Safari使用webkitSlice() $(function () { { var pause = false;//是否暂停 var $file; var $fileInput;//file input var $completedChunks = $('#completedChunks');//上传完成块数 var $progress = $('#progress');//上传进度条 var $percent = $('#percent');//上传百分比 var MiB = 1024 * 1024; var chunkSize = 8.56 * MiB;//xx MiB var chunkIndex = 0;//上传到的块 var $btnUpload = $('#btnUplaod'); var totalSize;//文件总大小 var totalSizeH;//文件总大小M var chunkCount;//分块数 var fileName;//文件名 $btnUpload.click(function () { var val = $.trim($(this).val()); if (val === 'Upload') { $fileInput = $('#file1'); $file = $fileInput[0].files[0]; if ($file === undefined) { $completedChunks.html('please select a file !'); return false; } totalSize = $file.size; chunkCount = Math.ceil(totalSize / chunkSize * 1.0); totalSizeH = (totalSize / MiB).toFixed(2); fileName = $file.name; val = 'Pause'; pause = false; chunkIndex = 0; } else if (val === 'Pause') { val = 'Resume'; pause = true; } else if (val === 'Resume') { val = 'Pause'; pause = false; } else { val = '-'; } $(this).val(val); postChunk(); }); function postChunk() { if (pause) return false; var isLastChunk = chunkIndex === chunkCount - 1; var fromSize = chunkIndex * chunkSize; var chunk = !isLastChunk ? $file.slice(fromSize, fromSize + chunkSize) : $file.slice(fromSize, totalSize); var fd = new FormData(); fd.append('chunk', chunk); fd.append('chunkIndex', chunkIndex); fd.append('chunkCount', chunkCount); fd.append('fileName', fileName); fd.append('__RequestVerificationToken', $("input[name=__RequestVerificationToken]").val()); $.ajax({ url: '/HtAdmin/Upload/SaveChunkFile', type: 'POST', data: fd, cache: false, contentType: false, processData: false, success: function (d) { if (!d.success) { $completedChunks.html(d.msg); return false; } chunkIndex = d.nextIndex; if (isLastChunk) { $completedChunks.html('combining .. '); $btnUpload.val('Upload').prop('disabled', true); //合并文件 $.post('/HtAdmin/Upload/CombineChunkFile', { fileName: fileName }, function (d) { $completedChunks.html(d.msg); $completedChunks.append('destFile: ' + d.destFile); $btnUpload.val('Upload').prop('disabled', false); $fileInput.val('');//清除文件 }); } else { postChunk();//递归上传文件块 //$completedChunks.html(chunkIndex + '/' + chunkCount ); $completedChunks.html((chunkIndex * chunkSize / MiB).toFixed(2) + 'M/' + totalSizeH + 'M'); } var completed = chunkIndex / chunkCount * 100; $percent.html(completed.toFixed(2) + '%').css('margin-left', parseInt(completed / 100 * $progress.width()) + 'px'); $progress.css('background', 'linear-gradient(to right, #ff0084 ' + completed + '%, #e8c5d7 ' + completed + '%)'); }, error: function (ex) { $completedChunks.html('ex:' + ex.responseText); } }); } } }); </script> } </div>
标签:Core,false,val,chunk,fileName,MVC,var,Net,chunkIndex From: https://www.cnblogs.com/m0616/p/17852579.html