asp.net core 上传大文件

起因

最近遇到一个上传视频大文件的需求,文件最少是1个G的视频文件.一直以来没做过这么大的上传文件的功能.原先都是限制上传大小.

在有HTML5了以后,可以对文件进行分割上传.这里是用plupload + Asp.Net Core来实现,其实是新瓶装老酒.

遇到的问题:
    1. 怎么给上传文件重命名呢? 是在前台处理呢? 还是后台处理呢?

前台页面代码

@{
    Layout = null;
}
<!DOCTYPE html>

<html>
<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width" />
    <title>Index</title>
</head>
<body>
    <div id="uploadContainer">
        <input type="button" value="选择文件" id="btnBrowse" />
        <input type="button" value="上传文件" id="btnUpload" />
        <ul id="fileList"></ul>
    </div>
    <script src="~/lib/jquery/dist/jquery.js"></script>
    <script src="~/js/plupload/plupload.full.min.js"></script>
    <script type="text/javascript">
        var $uploadContainer = $('#uploadContainer'),
            $fileList = $('#fileList'),
            $btnUpload = $('#btnUpload');

        //实例化一个上传对象
        var uploader = new plupload.Uploader({
            browse_button: 'btnBrowse',
            runtimes: 'html5,flash,silverlight,html4',  //兼容的上传方式
            url: '/UploadFile/UploadVideo',             //上传文件的地址
            flash_swf_url: 'plupload/Moxie.swf',
            sliverlight_xap_url: 'plupload/Moxie.xap',
            max_retries: 3,                             //允许重试次数
            chunk_size: '10mb',                         //分块大小
            multi_selection: false,                     //只能选择单个文件进行上传
            multipart_params: {
                uploadFileName: ''                      //大文件分块之后,要在接收action中修改文件名的话,会生成多个文件
            },
            filters: {
                mime_types: [
                    {
                        title: "视频文件",
                        extensions: "wmv,avi,mp4",    //只允许上传文件格式
                        max_file_size: '4096mb'       //允许上传文件的最大为4g
                    }
                ]
            }
        });

        //存储多个图片的url地址
        var urls = [];

        //存储触犯上传事件的事件对象
        var event;

        //初始化
        uploader.init();

        //绑定文件添加到队列的事件
        uploader.bind('FilesAdded', function (uploader, files) {
            var value = files[0];
            var fileName = value.name,
                html = '<li id="file-' + value.id + '">' +
                    '<p class="file-name">' + fileName + '</p>' +
                    '<p class="progress"></p>' +
                    '</li>';
            $fileList.html(html);
        });

        //绑定上传进度
        uploader.bind('UploadProgress', function (uploader, file) {
            $fileList.find('.progress').text(file.percent + '%');
        });

        //单个文件上传之后
        uploader.bind('FileUploaded', function (uploader, file, responseObject) {
            //从服务器返回图片url地址
            var url = responseObject.response;
            //先将url地址存储来,
            urls.push(url);
        });

        //文件完成上传事件
        uploader.bind('UploadComplete', function (uploader, files) {
            var value = urls[0];                     //取第1个元素
            var result = JSON.parse(value);          //转为json之后,判断是否全部上传完成
            if (result.isSuccess) {
                $.ajax({
                    type: 'POST',
                    dataType: 'json',
                    url: '/UploadFile/SaveVideoURL',
                    data: {
                        'videoURL': result.videoURL
                    },
                    success: function (data) {
                        if (data.isSuccess) {
                            alert('上传文件成功!');
                        }
                        else {
                            alert('上传文件失败!');
                        }
                    }
                });
            }

            //清空url数组
            urls = [];

            //清空显示列表
            $fileList.html('');
        });

        //上传事件
        $btnUpload.click(function (e) {
            event = e;
            uploader.setOption('multipart_params', {
                'uploadFileName': new Date().getTime()  //上传的时候,根据时间的毫秒数当作保存的文件名
            });
            uploader.start();
        });

        function getFileName() {
            return new Date().getTime();
        }
    </script>

</body>
</html>

后台代码

using System.IO;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc;

namespace qiufeng.core.Controllers
{
    public class UploadFileController : Controller
    {
        private IHostingEnvironment _hostEnv;
        public UploadFileController(IHostingEnvironment env)
        {
            _hostEnv = env;
        }

        /// <summary>
        /// 上传文件
        /// </summary>
        /// <returns></returns>
        public IActionResult Index()
        {
            return View();
        }

        /// <summary>
        /// 处理上传文件
        /// </summary>
        /// <returns></returns>
        [HttpPost]
        public IActionResult UploadVideo()
        {
            var files = Request.Form.Files;
            if (files != null)
            {
                string uploadFileName = Request.Form["uploadFileName"];  //获取要保存的文件名
                string fileName = Request.Form["name"];                  //上传的文件名
                string extName = Path.GetExtension(fileName);            //获取文件的扩展名
                fileName = $"{uploadFileName}{extName}";                 //合并要保存的文件名
                string filePath = Path.Combine(_hostEnv.ContentRootPath, "UploadFile", fileName);    //要保存的路径
                string videoURL = $"/UploadFile/{fileName}";

                //分块,第一次为0,根据这个标记不为0,开始对文件追加
                int chunk = string.IsNullOrEmpty(Request.Form["chunk"]) ? 0 : int.Parse(Request.Form["chunk"]); 
                foreach (var file in files)
                {
                    using (var stream = new FileStream(filePath, chunk == 0 ? FileMode.CreateNew : FileMode.Append))
                    {
                        file.CopyTo(stream);
                    }
                }
                return Json(new { isSuccess = true, videoURL = videoURL });
            }
            return Json(new { isSuccess = false, videoURL = "" });
        }

        /// <summary>
        /// 在大文件上传完成之后,关联保存的信息
        /// </summary>
        /// <returns></returns>
        [HttpPost]
        public IActionResult SaveVideoURL()
        {
            bool isSuccess = false;
            string videoURL = Request.Form["videoURL"];
            if (!string.IsNullOrEmpty(videoURL))
            {
                //对应关联有关联的id,这里只是模拟,要将videoURL保存起来
                isSuccess = true;
            }
            return Json(new { isSuccess = isSuccess });
        }
    }
}
秋风 2017-07-07