.NetCore+WebUploader實現大文件分片上傳


項目要求通過網站上傳大文件,比如視頻文件,通過摸索實現了文件分片來上傳,然后后台進行合並。

使用了開源的前台上傳插件WebUploader(http://fex.baidu.com/webuploader/)

WebUploader是由Baidu WebFE(FEX)團隊開發的一個簡單的以HTML5為主,FLASH為輔的現代文件上傳組件。在現代的瀏覽器里面能充分發揮HTML5的優勢,同時又不摒棄主流IE瀏覽器,沿用原來的FLASH運行時,兼容IE6+,iOS 6+, android 4+。兩套運行時,同樣的調用方式,可供用戶任意選用。

采用大文件分片並發上傳,極大的提高了文件上傳效率。

 

直接上代碼,前台cshtml

@{
    Layout = null;
}
<!DOCTYPE html>
<html>
<head>
    <meta name="viewport" content="width=device-width" />
    <link href="~/js/webuploader/webuploader.css" rel="stylesheet" />
    <link href="~/js/bootstrap.min.css" rel="stylesheet" />
    <script src="~/js/jquery.min.js"></script>
    <script src="~/js/webuploader/webuploader.js"></script>
    <title>Upload</title>
    <script>
        jQuery(function () {
            var $ = jQuery,
                $list = $('#thelist'),
                $btn = $('#ctlBtn'),
                state = 'pending',
                fileMd5,
                flag = true,
                dataState,
                fm = [],
                fnum,
                Token,
                uploader;
            var FileExt = ["mpg", "mpeg", "mp4", "avi"];
            Token = '@ViewBag.Token';
            if (Token == '' || Token== 'undefined')
            {
                $("#uploader").hide();
                alert("登錄超時,請重新登錄。");
            }
            //監聽分塊上傳過程中的三個時間點
            WebUploader.Uploader.register({
                "before-send-file": "beforeSendFile",
                "before-send": "beforeSend",
                "after-send-file": "afterSendFile",
            }, {
                    beforeSendFile: function (file) {
                        var startTime = new Date(file.lastModifiedDate);
                        fileName = file.name;
                        var deferred = WebUploader.Deferred(); 
                        (new WebUploader.Uploader()).md5File(file, 0, 10 * 1024 * 1024)
                            .progress(function (percentage) {
                                console.log("正在讀取文件");
                            })
                            .then(function (val) {
                                fileMd5 = val;
                                fm.push(fileMd5); 
                                deferred.resolve();
                            });
                        return deferred.promise();
                    },
                    //時間點2:如果有分塊上傳,則每個分塊上傳之前調用此函數
                    beforeSend: function (block) {
                        var deferred = WebUploader.Deferred();
 
                        //上傳前ajax檢測一下此文件塊是否已經上傳
 
                        this.owner.options.formData.fileMd5 = fileMd5;
                        this.owner.options.formData.chunk = block.chunk;
                        deferred.resolve();
                        return deferred.promise();
                    },
                    //時間點3:所有分塊上傳成功后調用此函數
                    afterSendFile: function (file) {
                        var deferred = $.Deferred();
                        $('#' + file.id).find('p.state').text('執行最后一步');
                        console.log(file);
                        console.log(file.guid);
                        $.ajax({
                            type: "POST",
                            url: "/api/v1/Check/FileMerge",
                            data: {
                                guid: file.guid,
                                fileMd5: fm[fnum],
                                fileName: file.name
                            },
                            cache: false,
                            async: false,
                            success: function (response) {
                                fnum++;
                                console.log(response);
                                if (response.success == true) {
                                    dataState = response;
                                    flag = true;
                                } else {
                                    flag = false;
                                }
                                deferred.resolve();
                            },
                            error: function () {
                                fnum++;
                                dataState = undefined;
                                flag = false;
                                deferred.reject();
                            }
                        });
 
                        return deferred.promise();
                    }
                });
            uploader = WebUploader.create({
                resize: false,
                fileNumLimit: 10,
                swf: '/js/Uploader.swf',
                server: '/api/v1/Check/FileSave',
                pick: '#picker',
                chunked: true,
                chunkSize: 10 * 1024 * 1024,
                chunkRetry: 5
                //, formData: {
                //    guid: GUID
                //}
            });
            uploader.on('beforeFileQueued', function (file) {
                var isAdd = false;
                for (var i = 0; i < FileExt.length; i++) {
                    if (file.ext == FileExt[i]) {
                        file.guid = WebUploader.Base.guid();
                        isAdd = true;
                        break;
                    }
                }
                return isAdd;
            });
            uploader.on('uploadBeforeSend', function (object, data, headers) {
                //console.log(object);
                headers.Authorization =Token;
                data.guid = object.file.guid;
            });
            // 當有文件添加進來的時候
            uploader.on('fileQueued', function (file) {
                $list.append('<div id="' + file.id + '" class="item">' +
                    '<h4 class="info">' + file.name + '</h4>' +
                    '<input type="hidden" id="h_' + file.id + '" value="' + file.guid + '" />' +
                    '<p class="state">等待上傳...</p>' +
                    '</div>');
            });
 
            // 文件上傳過程中創建進度條實時顯示。
            uploader.on('uploadProgress', function (file, percentage) {
                var $li = $('#' + file.id),
                    $percent = $li.find('.progress .progress-bar');
                // 避免重復創建
                if (!$percent.length) {
                    $percent = $('<div class="progress progress-striped active">' +
                        '<div class="progress-bar" role="progressbar" style="width: 0%">' +
                        '</div>' +
                        '</div>').appendTo($li).find('.progress-bar');
                }
                $li.find('p.state').text('上傳中');
 
                $percent.css('width', percentage * 100 + '%');
            });
 
            uploader.on('uploadSuccess', function (file) {
                if (dataState == undefined) {
                    $('#' + file.id).find('p.state').text('上傳失敗');
                    $('#' + file.id).find('button').remove();
                    $('#' + file.id).find('p.state').before('<button id="retry" type="button" class="btn btn-primary fright retry pbtn">重新上傳</button>');
                    flag = false;
                    file.setStatus('error');
                }
                if (dataState.success == true) {
                    $('#' + file.id).find('p.state').text('已上傳');
                    $('#' + file.id).find('button').remove();
 
                } else {
                    $('#' + file.id).find('p.state').text('上傳失敗');
                    flag = false;
                }
            });
 
            uploader.on('uploadError', function (file) {
                $('#' + file.id).find('p.state').text('上傳出錯');
            });
 
            uploader.on('uploadComplete', function (file) {
                $('#' + file.id).find('.progress').fadeOut();
            });
 
            uploader.on('all', function (type) {
                if (type === 'startUpload') {
                    state = 'uploading';
                } else if (type === 'stopUpload') {
                    state = 'paused';
                } else if (type === 'uploadFinished') {
                    state = 'done';
                }
                if (state === 'done') {
                    $btn.text('繼續上傳');
                } else if (state === 'uploading') {
                    $btn.text('暫停上傳');
                } else {
                    $btn.text('開始上傳');
                }
            });
            $btn.on('click', function () {
                if (state === 'uploading') {
                    uploader.stop();
                } else if (state == 'done') {
                    window.location.reload();
                }
                else {
                    uploader.upload();
                }
            });
        });
    </script>
</head>
<body>
    <div id="uploader" class="wu-example">
        <span style="color:red">只能上傳mpg、mpeg、mp4、avi格式的視頻文件</span>
        <div id="thelist" class="uploader-list"></div>
        <div class="btns">
            <div id="picker" class="webuploader-container"><div class="webuploader-pick">選擇文件</div><div style="position: absolute; top: 0px; left: 0px; width: 88px; height: 34px; overflow: hidden; bottom: auto; right: auto;"><input type="file" name="file" class="webuploader-element-invisible" multiple="multiple"><label style="opacity: 0; width: 100%; height: 100%; display: block; cursor: pointer; background: rgb(255, 255, 255);"></label></div></div>
            <button id="ctlBtn" class="btn btn-default">開始上傳</button> 
        </div>
    </div>
</body>
</html>

后台代碼:

#region 上傳視頻
        public IActionResult Upload()
        {
            ViewBag.Token = HttpContext.Request.Headers["Authorization"];//獲取認證信息,傳遞給前台,方便Ajax請求時提供
            return View();
        }
        /// <summary>
        /// 上傳文件
        /// </summary>
        /// <returns></returns>
        [HttpPost]
        public async Task<IActionResult> FileSave()
        {
            var date = Request;
            var files = Request.Form.Files;
            long size = files.Sum(f => f.Length);
            foreach (var formFile in files)
            {
                if (formFile.Length > 0)
                {
                    string fileExt = formFile.FileName.Substring(formFile.FileName.IndexOf('.')); //文件擴展名,不含“.”
                    long fileSize = formFile.Length; //獲得文件大小,以字節為單位
                    //string newFileName = Guid.NewGuid().ToString() + "." + fileExt; //隨機生成新的文件名
                    string DirPath = Path.Combine(_uploadConfig.TmpPath, Request.Form["guid"]);
                    if (!Directory.Exists(DirPath))
                    {
                        Directory.CreateDirectory(DirPath);
                    }
                    var filePath = DirPath + "/" + Request.Form["chunk"] + fileExt;
                    using (var stream = new FileStream(filePath, FileMode.Create))
                    {
                        await formFile.CopyToAsync(stream);
 
                    }
                }
            }
            return Ok(new { count = files.Count, size });
        }
 
        /// <summary>
        /// 合並請求
        /// </summary>
        /// <returns></returns>
        [HttpPost]
        public async Task<IActionResult> FileMerge()
        {
            bool ok = false;
            string errmsg = "";
            try
            {
                var temporary = Path.Combine(_uploadConfig.TmpPath, Request.Form["guid"]);//臨時文件夾
                string fileName = Request.Form["fileName"];//文件名
                string fileExt = Path.GetExtension(fileName);//獲取文件后綴
                var files = Directory.GetFiles(temporary);//獲得下面的所有文件
              
                var finalFilePath = Path.Combine(_uploadConfig.UpLoadPath + fileName);//最終的文件名
                //var fs = new FileStream(finalFilePath, FileMode.Create);
                using (var fs = new FileStream(finalFilePath, FileMode.Create))
                {
                    foreach (var part in files.OrderBy(x => x.Length).ThenBy(x => x))
                    {
                        var bytes = System.IO.File.ReadAllBytes(part);
                        await fs.WriteAsync(bytes, 0, bytes.Length);
                        bytes = null;
                        System.IO.File.Delete(part);//刪除分塊
                    }
                    Directory.Delete(temporary);//刪除文件夾
                    ok = true;
                }
            }
            catch (Exception ex)
            {
                ok = false;
                errmsg = ex.Message;
                log4net.Error(errmsg);
            }
            if (ok)
            {
                return Ok(new { success = true, msg = "" });
            }
            else
            {
                return Ok(new { success = false, msg = errmsg }); ;
            }
        }
 
        #endregion

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM