Asp.Net Core 轻松学-一行代码搞定文件上传 JSONHelper
Asp.Net Core 轻松学-一行代码搞定文件上传
前言
在 Web 应用程序开发过程中,总是无法避免涉及到文件上传,这次我们来聊一聊怎么去实现一个简单方便可复用文件上传功能;通过创建自定义绑定模型来实现文件上传。
1. 实现自定义绑定模型
- 1.1 在 Asp.Net Core MVC 中,内置了很多种绑定模型,让我们可以很方便的去使用,比如下面常用的几种绑定模型
FromBodyAttribute
FromFromAttribute
FromQueryAttribute
FromHeaderAttribute
FromServicesAttribute
FromRouteAttribute
- 常见用法比如
[HttpPost]
public async Task<IActionResult> PostInfo([FromBody]UserInfo user,[FromQuery] string city)
{
...
}
- 查看以上绑定模型,唯独缺少一个 FromFileAttribute ,下面就来实现一个自己的 FromFileAttribute
public class FromFileAttribute : Attribute, IBindingSourceMetadata
{
public BindingSource BindingSource => BindingSource.FormFile;
}
- 非常简单,就三行代码,完全照抄系统内置的绑定模型,唯一不同的就是指定 BindingSource 为 BindingSource.FormFile。
2. 实现一个上传文件实体类,专门用于接收客户端参数
- 2.1 创建 UserFile
public class UserFile
{
public string FileName { get; set; }
public long Length { get; set; }
public string Extension { get; set; }
public string FileType { get; set; }
private readonly static string[] Filters = { ".jpg", ".png", ".bmp" };
public bool IsValid => !string.IsNullOrEmpty(this.Extension) && Filters.Contains(this.Extension);
private IFormFile file;
public IFormFile File
{
get { return file; }
set
{
if (value != null)
{
this.file = value;
this.FileType = this.file.ContentType;
this.Length = this.file.Length;
this.Extension = this.file.FileName.Substring(file.FileName.LastIndexOf('.'));
if (string.IsNullOrEmpty(this.FileName))
this.FileName = this.FileName;
}
}
}
public async Task<string> SaveAs(string destinationDir = null)
{
if (this.file == null)
throw new ArgumentNullException("没有需要保存的文件");
if (destinationDir != null)
Directory.CreateDirectory(destinationDir);
var newName = DateTime.Now.Ticks;
var newFile = Path.Combine(destinationDir ?? "", $"{newName}{this.Extension}");
using (FileStream fs = new FileStream(newFile, FileMode.CreateNew))
{
await this.file.CopyToAsync(fs);
fs.Flush();
}
return newFile;
}
}
- UserFile 是一个带保持文件行为的实体类,该类的公共属性用于从表单域中接收和属性名称相同的表单值,其中公共属性 File 用于接收文件,并在设置值的时候去做一些其它属性初始化的工作,比如文件长度和扩展名、文件类型
- 其中还实现了一个简单的文件过滤器,判断客户端上传的文件是否属于服务端允许上传的文件扩展名
- 最后 SaveAs(string destinationDir = null) 通过传入指定目录,将文件保存,并返回保存后的文件绝对路径
3. 上传文件
- 3.1 下面就定义一个简单的 API 接口,用于测试上传文件
[HttpPost]
public async Task<IActionResult> Post([FromFile]UserFile file)
{
if (file == null || !file.IsValid)
return new JsonResult(new { code = 500, message = "不允许上传的文件类型" });
string newFile = string.Empty;
if (file != null)
newFile = await file.SaveAs("/data/files/images");
return new JsonResult(new { code = 0, message = "成功", url = newFile });
}
-
3.2 首先是在 Post([FromFile]UserFile file) 中使用上面创建的 FromFileAttribute 对模型 UserFile 进行绑定,然后验证文件是否正确,接下来通过 file.SaveAs("/data/files/images"); 保存文件
-
3.3 上传代码非常简单,几乎到了无法精简的程度,最终发挥作用的就是 file.SaveAs 操作
4. 上传测试
- 4.1 现在通过控制台启动服务
- 4.2 使用 Postman 模拟表单上传文件
- 4.3 上传成功,现在来查看目录下是否有文件
结语
- 在上传表单中,我们定义了附件的名称为 file 对应绑定模型的公共属性 File,这样模型就可以自动获得该文件
- 表单中还传递了另外一个字段 filename,对应绑定模型的公共属性 FileName,实现自定义文件友好显示名称
- 通过自定义模型绑定,实现了快速上传文件功能,该功能只能用于上传小文件,对于大文件,还是需要实现分片上传,或者使用 CDN 等服务商的接口
示例代码下载
https://files.cnblogs.com/files/viter/Ron.UploadFile.zip
JSONHelper
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.IO;
using System.Text;
using System.Web.Script.Serialization;
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
|
<br><br>[Serializable] public static class JSONHelper { /// <summary> /// 对象转JSON /// </summary> /// <param name="obj">对象</param> /// <returns>JSON格式的字符串</returns> public static string ObjectToJSON( object obj) { JavaScriptSerializer jss = new JavaScriptSerializer(); try { return jss.Serialize(obj); } catch { } return null ; } /// <summary> /// /// JSON文本转对象,泛型方法 /// </summary> /// <typeparam name="T">类型</typeparam> /// <param name="jsonText">JSON文本</param> /// <returns>指定类型的对象</returns> public static T JSONToObject<T>( string jsonText) { JavaScriptSerializer jss = new JavaScriptSerializer(); try { return jss.Deserialize<T>(jsonText); } catch {} return default (T); } public static string ObjectToJsonDateTime( object obj) { JavaScriptSerializer jss = new JavaScriptSerializer(); jss.RegisterConverters( new JavaScriptConverter[] { new DateTimeConverter() }); try { return jss.Serialize(obj); } catch { } return null ; } }<br><br><br> |
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
|
using System; using System.Collections; using System.Collections.Generic; using System.Linq; using System.Text; using System.Web.Script.Serialization; public class DateTimeConverter : JavaScriptConverter { public override object Deserialize(IDictionary< string , object > dictionary, Type type, JavaScriptSerializer serializer) { return new JavaScriptSerializer().ConvertToType(dictionary, type); } public override IDictionary< string , object > Serialize( object obj, JavaScriptSerializer serializer) { if (!(obj is DateTime)) { return null ; } return new CustomString(((DateTime)obj).ToString( "yyyy-MM-dd HH:mm:ss" )); } public override IEnumerable<Type> SupportedTypes { get { return new [] { typeof (DateTime) }; } } private class CustomString : Uri, IDictionary< string , object > { public CustomString( string str) : base (str, UriKind.Relative) { } void IDictionary< string , object >.Add( string key, object value) { throw new NotImplementedException(); } bool IDictionary< string , object >.ContainsKey( string key) { throw new NotImplementedException(); } ICollection< string > IDictionary< string , object >.Keys { get { throw new NotImplementedException(); } } bool IDictionary< string , object >.Remove( string key) { throw new NotImplementedException(); } bool IDictionary< string , object >.TryGetValue( string key, out object value) { throw new NotImplementedException(); } ICollection< object > IDictionary< string , object >.Values { get { throw new NotImplementedException(); } } object IDictionary< string , object >. this [ string key] { get { throw new NotImplementedException(); } set { throw new NotImplementedException(); } } void ICollection<KeyValuePair< string , object >>.Add(KeyValuePair< string , object > item) { throw new NotImplementedException(); } void ICollection<KeyValuePair< string , object >>.Clear() { throw new NotImplementedException(); } bool ICollection<KeyValuePair< string , object >>.Contains(KeyValuePair< string , object > item) { throw new NotImplementedException(); } void ICollection<KeyValuePair< string , object >>.CopyTo(KeyValuePair< string , object >[] array, int arrayIndex) { throw new NotImplementedException(); } int ICollection<KeyValuePair< string , object >>.Count { get { throw new NotImplementedException(); } } bool ICollection<KeyValuePair< string , object >>.IsReadOnly { get { throw new NotImplementedException(); } } bool ICollection<KeyValuePair< string , object >>.Remove(KeyValuePair< string , object > item) { throw new NotImplementedException(); } IEnumerator<KeyValuePair< string , object >> IEnumerable<KeyValuePair< string , object >>.GetEnumerator() { throw new NotImplementedException(); } IEnumerator IEnumerable.GetEnumerator() { throw new NotImplementedException(); } } } |