.NetCore+WebUploader实现大文件分片上传

感谢大哥   https://www.cnblogs.com/wdw984/p/11725118.html

项目要求通过网站上传大文件,比如视频文件,通过摸索实现了文件分片来上传,然后后台进行合并。

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

WebUploader是由Baidu WebFE(FEX)团队开发的一个简单的以HTML5为主,FLASH为辅的现代文件上传组件。在现代的浏览器里面能充分发挥HTML5的优势,同时又不摒弃主流IE浏览器,沿用原来的FLASH运行时,兼容IE6+,iOS 6+, android 4+。两套运行时,同样的调用方式,可供用户任意选用。

采用大文件分片并发上传,极大的提高了文件上传效率。

直接上代码,前台cshtml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
@{
    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=" 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; 88px; height: 34px; overflow: hidden; bottom: auto; right: auto;"><input type="file" name="file" class="webuploader-element-invisible" multiple="multiple"><label style="opacity: 0; 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>

后台代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
#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
原文地址:https://www.cnblogs.com/ning-xiaowo/p/13153678.html