本章和你們分享的是.NetCore的MVC框架上傳文件的示例,主要講的內容有:form方式提交上傳,ajax上傳,ajax提交+上傳進度效果,Task並行處理+ajax提交+上傳進度html
上傳文件必需要設置form元素裏面的 enctype="multipart/form-data" 屬性和post方式,若是你想要多選上傳文件的話,須要把文件type='file'元素設置她的屬性multiple='multiple',內容:前端
1
2
3
4
5
6
7
8
|
<
form
class
=
"form-horizontal"
action
=
"/Home/FileUp"
method
=
"post"
enctype
=
"multipart/form-data"
>
<
input
type
=
"file"
name
=
"MyPhoto"
class
=
"form-control"
multiple />
<
br
/>
<
button
class
=
"btn btn-default"
>form上傳</
button
>
<
br
/>
<
span
style
=
"color:red"
>@ViewData["MsgBox"]</
span
>
</
form
>
|
因爲採用form提交,這個測試用例只接用了button元素默認的type=submit來提交表單,對應的後臺Action中代碼以下:html5
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
|
/// <summary>
/// form提交上傳
/// </summary>
/// <param name="user"></param>
/// <returns></returns>
[HttpPost]
public
async Task<IActionResult> FileUp(MoUser user)
{
if
(user.MyPhoto ==
null
|| user.MyPhoto.Count <= 0) { MsgBox(
"請上傳圖片。"
);
return
View(); }
//var file = Request.Form.Files;
foreach
(
var
file
in
user.MyPhoto)
{
var
fileName = file.FileName;
var
contentType = file.ContentType;
var
len = file.Length;
var
fileType =
new
string
[] {
"image/jpeg"
,
"image/png"
};
if
(!fileType.Any(b => b.Contains(contentType))) { MsgBox($
"只能上傳{string.Join("
,
", fileType)}格式的圖片。"
);
return
View(); }
if
(len > 1024 * 1024 * 4) { MsgBox(
"上傳圖片大小隻能在4M如下。"
);
return
View(); }
var
path = Path.Combine(
@"D:\F\學習\vs2017\netcore\netcore01\WebApp01\wwwroot\myfile"
, fileName);
using
(
var
stream = System.IO.File.Create(path))
{
await file.CopyToAsync(stream);
}
}
MsgBox($
"上傳成功"
);
return
View();
}
|
從前端到後端的Action不得不說這種form表單提交的方式挺簡單的,須要注意的是Action這裏用的實體模型方式來對應上傳的文件信息,這裏自定義了MoUser類,經過屬性 public List<IFormFile> MyPhoto { get; set; } 來匹配html表單中文件type='file'的name屬性名稱name="MyPhoto":jquery
1
2
3
4
5
6
7
|
public
class
MoUser
{
public
int
UserId {
get
;
set
; } = 1;
public
string
UserName {
get
;
set
; } =
"神牛步行3"
;
public
List<IFormFile> MyPhoto {
get
;
set
; }
}
|
這樣就能經過實體模型的方式把上傳的文件信息存儲在自定義MoUser類中的MyPhoto屬性中了;ajax
這裏須要在上面例子中的html處修改一些東西,再也不使用form提交,指定了普通button按鈕來觸發ajax的提交,完整html代碼如:c#
1
2
3
4
5
6
7
8
|
<
form
class
=
"form-horizontal"
id
=
"form01"
method
=
"post"
enctype
=
"multipart/form-data"
>
<
input
type
=
"file"
name
=
"MyPhoto01"
class
=
"form-control"
multiple />
<
br
/>
<
button
type
=
"button"
id
=
"btnAjax"
class
=
"btn btn-default"
>ajax上傳</
button
>
<
br
/>
<
span
style
=
"color:red"
id
=
"span01"
></
span
>
</
form
>
|
有了佈局,再來看看具體的js實現代碼,這裏我採用jquery的ajax提交的方法來操做,也用到了html5新增的FormData來存儲表單的數據:後端
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
$(
"#btnAjax"
).on(
"click"
,
function
() {
var
msg = $(
"#span01"
);
var
form = document.getElementById(
"form01"
);
//console.log(form);
var
data =
new
FormData(form);
$.ajax({
type:
"POST"
,
url:
"/home/AjaxFileUp"
,
data: data,
contentType:
false
,
processData:
false
,
success:
function
(data) {
if
(data) {
msg.html(data.msg);
}
},
error:
function
() {
msg.html(
"上傳文件異常,請稍後重試!"
);
}
});
});
|
至於後臺Action的方法和示例一的相差不大,關鍵點在於這裏我直接使用 Request.Form.Files 方式來獲取上傳的全部文件,再也不使用實體模型的方式了,這樣測試用例更多樣化吧:數組
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
|
/// <summary>
/// ajax無上傳進度效果上傳
/// </summary>
/// <returns></returns>
[HttpPost]
public
async Task<JsonResult> AjaxFileUp()
{
var
data =
new
MoData { Msg =
"上傳失敗"
};
try
{
var
files = Request.Form.Files.Where(b => b.Name ==
"MyPhoto01"
);
//非空限制
if
(files ==
null
|| files.Count() <= 0) { data.Msg =
"請選擇上傳的文件。"
;
return
Json(data); }
//格式限制
var
allowType =
new
string
[] {
"image/jpeg"
,
"image/png"
};
if
(files.Any(b => !allowType.Contains(b.ContentType)))
{
data.Msg = $
"只能上傳{string.Join("
,
", allowType)}格式的文件。"
;
return
Json(data);
}
//大小限制
if
(files.Sum(b => b.Length) >= 1024 * 1024 * 4)
{
data.Msg =
"上傳文件的總大小隻能在4M如下。"
;
return
Json(data);
}
//寫入服務器磁盤
foreach
(
var
file
in
files)
{
var
fileName = file.FileName;
var
path = Path.Combine(
@"D:\F\學習\vs2017\netcore\netcore01\WebApp01\wwwroot\myfile"
, fileName);
using
(
var
stream = System.IO.File.Create(path))
{
await file.CopyToAsync(stream);
}
}
data.Msg =
"上傳成功"
;
data.Status = 2;
}
catch
(Exception ex)
{
data.Msg = ex.Message;
}
return
Json(data);
}
|
若是你有耐心讀到這裏,那麼後面的內容我的感受對你開發會有好的幫助,不負你期待;緩存
一樣咱們先來看對應的html代碼,其實和示例2幾乎同樣,只是把名稱變更了下:服務器
1
2
3
4
5
6
7
8
|
<
form
class
=
"form-horizontal"
id
=
"form02"
method
=
"post"
enctype
=
"multipart/form-data"
>
<
input
type
=
"file"
name
=
"MyPhoto02"
class
=
"form-control"
multiple />
<
br
/>
<
button
type
=
"button"
id
=
"btnAjax02"
class
=
"btn btn-default"
>ajax上傳進度效果上傳</
button
>
<
br
/>
<
span
style
=
"color:red"
id
=
"span02"
></
span
>
</
form
>
|
要加一個進度效果,須要用到js的定時器,定時獲取上傳文件的上傳進度數據信息,所以這裏經過js的setInterval方法來定時請求一個進度數據接口,注意用完以後須要清除這個定時器,否則一直再不斷請求您接口:
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
|
$("#btnAjax02").on("click", function () {
var interBar;
var msg = $("#span02");
msg.html("上傳中,請稍後...");
var form = document.getElementById("form02");
//console.log(form);
var data = new FormData(form);
$.ajax({
type: "POST",
url: "/home/AjaxFileUp02",
data: data,
contentType: false,
processData: false,
success: function (data) {
if (data) {
msg.html(data.msg);
//清除進度查詢
if (interBar) { clearInterval(interBar); }
}
},
error: function () {
msg.html("上傳文件異常,請稍後重試!");
if (interBar) { clearInterval(interBar); }
}
});
//獲取進度
interBar = setInterval(function () {
$.post("/home/ProgresBar02", function (data) {
if (data) {
var isClearVal = true;
var strArr = [];
$.each(data, function (i, item) {
strArr.push('文件:' + item.fileName + ",當前上傳:" + item.percentBar + '<
br
/>');
if (item.status != 2) { isClearVal = false; }
});
msg.html(strArr.join(''));
if (isClearVal) {
if (interBar) { clearInterval(interBar); }
}
}
});
}, 200);
});
|
既然上面說到單獨的進度數據接口,那麼咱們除了上傳Action外,也須要進度的Action,而這進度Action獲得的上傳文件數據信息必須和上傳的Action一直,所以就須要用到緩存等存儲數據的方式,這裏我用的是MemoryCache的方式,對已netcore來講僅僅只須要在起始文件(如:Startup.cs)中添加組件服務:
1
2
3
4
5
6
7
8
|
public
void
ConfigureServices(IServiceCollection services)
{
// Add framework services.
services.AddMvc();
//添加cache支持
services.AddDistributedMemoryCache();
}
|
而後經過構造函數注入到對應的接口Controller中去:
1
2
3
4
5
6
7
8
|
readonly
IMemoryCache _cache;
public
HomeController(IOptions<MoOptions> options, ILogger<HomeController> logger, IMemoryCache cache)
{
this
._options = options.Value;
_logger = logger;
_cache = cache;
}
|
到此咱們就能利用cache來存儲咱們上傳進度信息了,來看下處理上傳的Action:
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
|
private
string
cacheKey =
"UserId_UpFile"
;
private
string
cacheKey03 =
"UserId_UpFile03"
;
/// <summary>
/// ajax上傳進度效果上傳
/// </summary>
/// <returns></returns>
[HttpPost]
public
async Task<JsonResult> AjaxFileUp02()
{
var
data =
new
MoData { Msg =
"上傳失敗"
};
try
{
var
files = Request.Form.Files.Where(b => b.Name ==
"MyPhoto02"
);
//非空限制
if
(files ==
null
|| files.Count() <= 0) { data.Msg =
"請選擇上傳的文件。"
;
return
Json(data); }
//格式限制
var
allowType =
new
string
[] {
"image/jpeg"
,
"image/png"
};
if
(files.Any(b => !allowType.Contains(b.ContentType)))
{
data.Msg = $
"只能上傳{string.Join("
,
", allowType)}格式的文件。"
;
return
Json(data);
}
//大小限制
if
(files.Sum(b => b.Length) >= 1024 * 1024 * 4)
{
data.Msg =
"上傳文件的總大小隻能在4M如下。"
;
return
Json(data);
}
//初始化上傳多個文件的Bar,存儲到緩存中,方便獲取上傳進度
var
listBar =
new
List<MoBar>();
files.ToList().ForEach(b =>
{
listBar.Add(
new
MoBar
{
FileName = b.FileName,
Status = 1,
CurrBar = 0,
TotalBar = b.Length
});
});
_cache.Set<List<MoBar>>(cacheKey, listBar);
//寫入服務器磁盤
foreach
(
var
file
in
files)
{
//總大小
var
totalSize = file.Length;
//初始化每次讀取大小
var
readSize = 1024L;
var
bt =
new
byte
[totalSize > readSize ? readSize : totalSize];
//當前已經讀取的大小
var
currentSize = 0L;
var
fileName = file.FileName;
var
path = Path.Combine(
@"D:\F\學習\vs2017\netcore\netcore01\WebApp01\wwwroot\myfile"
, fileName);
using
(
var
stream = System.IO.File.Create(path))
{
//await file.CopyToAsync(stream);
//進度條處理流程
using
(
var
inputStream = file.OpenReadStream())
{
//讀取上傳文件流
while
(await inputStream.ReadAsync(bt, 0, bt.Length) > 0)
{
//當前讀取的長度
currentSize += bt.Length;
//寫入上傳流到服務器文件中
await stream.WriteAsync(bt, 0, bt.Length);
//獲取每次讀取的大小
readSize = currentSize + readSize <= totalSize ?
readSize :
totalSize - currentSize;
//從新設置
bt =
new
byte
[readSize];
//設置當前上傳的文件進度,並從新緩存到進度緩存中
var
bars = _cache.Get<List<MoBar>>(cacheKey);
var
currBar = bars.Where(b => b.FileName == fileName).SingleOrDefault();
currBar.CurrBar = currentSize;
currBar.Status = currentSize >= totalSize ? 2 : 1;
_cache.Set<List<MoBar>>(cacheKey, bars);
System.Threading.Thread.Sleep(1000 * 1);
}
}
}
}
data.Msg =
"上傳完成"
;
data.Status = 2;
}
catch
(Exception ex)
{
data.Msg = ex.Message;
}
return
Json(data);
}
|
代碼一會兒就變多了,其實按照邏輯來講增長了存儲進度的Cache,和逐一讀取上傳文件流的邏輯而已,具體你們能夠仔細看下代碼,都有備註說明;再來就是咋們的進度信息Action接口:
1
2
3
4
5
6
7
8
9
10
11
12
13
|
[HttpPost]
public
JsonResult ProgresBar02()
{
var
bars =
new
List<MoBar>();
try
{
bars = _cache.Get<List<MoBar>>(cacheKey);
}
catch
(Exception ex)
{
}
return
Json(bars);
}
|
進度接口只須要獲取cache中的進度信息就好了,注:這裏是測試用例,具體使用場景請各位自行增長其餘邏輯代碼;下面就來看下效果截圖:
這一小節,將會使用Task來處理上傳的文件,經過上一小節截圖可以看出,若是你上傳多個文件,那麼都是按照次序一個一個讀取文件流來生成上傳文件到服務器,這裏改良一下利用Task的特色,就能實現同時讀取不一樣文件流了,先來看下html代碼和js代碼:
1
2
3
4
5
6
7
8
|
<
form
class
=
"form-horizontal"
id
=
"form03"
method
=
"post"
enctype
=
"multipart/form-data"
>
<
input
type
=
"file"
name
=
"MyPhoto03"
class
=
"form-control"
multiple />
<
br
/>
<
button
type
=
"button"
id
=
"btnAjax03"
class
=
"btn btn-default"
>task任務處理ajax上傳進度效果上傳</
button
>
<
br
/>
<
span
style
=
"color:red"
id
=
"span03"
></
span
>
</
form
>
|
因爲和示例3的js代碼無差異這裏我直接貼出代碼:
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
|
$(
"#btnAjax03"
).on(
"click"
,
function
() {
var
interBar;
var
msg = $(
"#span03"
);
msg.html(
"上傳中,請稍後..."
);
var
form = document.getElementById(
"form03"
);
//console.log(form);
var
data =
new
FormData(form);
$.ajax({
type:
"POST"
,
url:
"/home/AjaxFileUp03"
,
data: data,
contentType:
false
,
processData:
false
,
success:
function
(data) {
if
(data) {
msg.html(data.msg);
//清除進度查詢
if
(interBar) { clearInterval(interBar); }
}
},
error:
function
() {
msg.html(
"上傳文件異常,請稍後重試!"
);
if
(interBar) { clearInterval(interBar); }
}
});
//獲取進度
interBar = setInterval(
function
() {
$.post(
"/home/ProgresBar03"
,
function
(data) {
if
(data) {
var
isClearVal =
true
;
var
strArr = [];
$.each(data,
function
(i, item) {
strArr.push(
'文件:'
+ item.fileName +
",當前上傳:"
+ item.percentBar +
'<br/>'
);
if
(item.status != 2) { isClearVal =
false
; }
});
msg.html(strArr.join(
''
));
if
(isClearVal) {
if
(interBar) { clearInterval(interBar); }
}
}
});
}, 200);
});
|
關鍵點在後臺,經過task數組來存儲每一個上傳文件的處理任務 Task[] tasks = new Task[len]; ,而後使用 Task.WaitAll(tasks); 等待全部上傳任務的完成,這裏特別注意了這裏必須等待,否則會丟失上傳文件流(屢次測試結果):
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
|
/// <summary>
/// ajax上傳進度效果上傳
/// </summary>
/// <returns></returns>
[HttpPost]
public
JsonResult AjaxFileUp03()
{
var
data =
new
MoData { Msg =
"上傳失敗"
};
try
{
var
files = Request.Form.Files.Where(b => b.Name ==
"MyPhoto03"
);
//非空限制
if
(files ==
null
|| files.Count() <= 0) { data.Msg =
"請選擇上傳的文件。"
;
return
Json(data); }
//格式限制
var
allowType =
new
string
[] {
"image/jpeg"
,
"image/png"
};
if
(files.Any(b => !allowType.Contains(b.ContentType)))
{
data.Msg = $
"只能上傳{string.Join("
,
", allowType)}格式的文件。"
;
return
Json(data);
}
//大小限制
if
(files.Sum(b => b.Length) >= 1024 * 1024 * 4)
{
data.Msg =
"上傳文件的總大小隻能在4M如下。"
;
return
Json(data);
}
//初始化上傳多個文件的Bar,存儲到緩存中,方便獲取上傳進度
var
listBar =
new
List<MoBar>();
files.ToList().ForEach(b =>
{
listBar.Add(
new
MoBar
{
FileName = b.FileName,
Status = 1,
CurrBar = 0,
TotalBar = b.Length
});
});
_cache.Set<List<MoBar>>(cacheKey03, listBar);
var
len = files.Count();
Task[] tasks =
new
Task[len];
//寫入服務器磁盤
for
(
int
i = 0; i < len; i++)
{
var
file = files.Skip(i).Take(1).SingleOrDefault();
tasks[i] = Task.Factory.StartNew((p) =>
{
var
item = p
as
IFormFile;
//總大小
var
totalSize = item.Length;
//初始化每次讀取大小
var
readSize = 1024L;
var
bt =
new
byte
[totalSize > readSize ? readSize : totalSize];
//當前已經讀取的大小
var
currentSize = 0L;
var
fileName = item.FileName;
var
path = Path.Combine(
@"D:\F\學習\vs2017\netcore\netcore01\WebApp01\wwwroot\myfile"
, fileName);
using
(
var
stream = System.IO.File.Create(path))
{
//進度條處理流程
using
(
var
inputStream = item.OpenReadStream())
{
//讀取上傳文件流
while
(inputStream.Read(bt, 0, bt.Length) > 0)
{
//當前讀取的長度
currentSize += bt.Length;
//寫入上傳流到服務器文件中
stream.Write(bt, 0, bt.Length);
//獲取每次讀取的大小
readSize = currentSize + readSize <= totalSize ?
readSize :
totalSize - currentSize;
//從新設置
bt =
new
byte
[readSize];
//設置當前上傳的文件進度,並從新緩存到進度緩存中
var
bars = _cache.Get<List<MoBar>>(cacheKey03);
var
currBar = bars.Where(b => b.FileName == fileName).SingleOrDefault();
currBar.CurrBar = currentSize;
currBar.Status = currentSize >= totalSize ? 2 : 1;
_cache.Set<List<MoBar>>(cacheKey03, bars);
System.Threading.Thread.Sleep(1000 * 1);
}
}
}
}, file);
}
//任務等待 ,這裏必須等待,否則會丟失上傳文件流
Task.WaitAll(tasks);
data.Msg =
"上傳完成"
;
data.Status = 2;
}
catch
(Exception ex)
{
data.Msg = ex.Message;
}
return
Json(data);
}
|
至於獲取上傳進度的Action也僅僅只是讀取緩存數據而已:
1
2
3
4
5
6
7
8
9
10
11
12
13
|
[HttpPost]
public
JsonResult ProgresBar03()
{
var
bars =
new
List<MoBar>();
try
{
bars = _cache.Get<List<MoBar>>(cacheKey03);
}
catch
(Exception ex)
{
}
return
Json(bars);
}
|
這裏再給出上傳進度的實體類:
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
|
public
class
MoData
{
/// <summary>
/// 0:失敗 1:上傳中 2:成功
/// </summary>
public
int
Status {
get
;
set
; }
public
string
Msg {
get
;
set
; }
}
public
class
MoBar : MoData
{
/// <summary>
/// 文件名字
/// </summary>
public
string
FileName {
get
;
set
; }
/// <summary>
/// 當前上傳大小
/// </summary>
public
long
CurrBar {
get
;
set
; }
/// <summary>
/// 總大小
/// </summary>
public
long
TotalBar {
get
;
set
; }
/// <summary>
/// 進度百分比
/// </summary>
public
string
PercentBar
{
get
{
return
$
"{(this.CurrBar * 100 / this.TotalBar)}%"
;
}
}
}
|
到此task任務處理上傳文件的方式就完成了,咋們來看圖看效果吧:
可以經過示例3和4的效果圖對比出,沒使用Task和使用的效果區別,這樣效果您值得擁有,耐心讀完本文內容的朋友,內容最後附上具體測試用例代碼:.NetCore上傳多文件的幾種示例