.Net Core MVC超大文件上传

发布时间 2023-11-23 21:52:14作者: 假装摸鱼
后端控制器:
//
用于保存的文件夹 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); } }
@{
    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>