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)
    {
        ...
    }
    1. 查看以上绑定模型,唯独缺少一个 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<stringobject> dictionary, Type type, JavaScriptSerializer serializer)
        {
            return new JavaScriptSerializer().ConvertToType(dictionary, type);
        }
 
        public override IDictionary<stringobject> 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<stringobject>
        {
            public CustomString(string str) : base(str, UriKind.Relative)
            {
            }
 
            void IDictionary<stringobject>.Add(string key, object value)
            {
                throw new NotImplementedException();
            }
 
            bool IDictionary<stringobject>.ContainsKey(string key)
            {
                throw new NotImplementedException();
            }
 
            ICollection<string> IDictionary<stringobject>.Keys
            {
                get
                {
                    throw new NotImplementedException();
                }
 
            }
 
            bool IDictionary<stringobject>.Remove(string key)
            {
                throw new NotImplementedException();
            }
 
 
            bool IDictionary<stringobject>.TryGetValue(string key, out object value)
            {
                throw new NotImplementedException();
 
            }
 
            ICollection<object> IDictionary<stringobject>.Values
            {
 
                get
                {
                    throw new NotImplementedException();
                }
 
            }
 
            object IDictionary<stringobject>.this[string key]
            {
                get
                {
                    throw new NotImplementedException();
                }
                set
                {
                    throw new NotImplementedException();
                }
            }
 
            void ICollection<KeyValuePair<stringobject>>.Add(KeyValuePair<stringobject> item)
            {
                throw new NotImplementedException();
            }
 
            void ICollection<KeyValuePair<stringobject>>.Clear()
            {
                throw new NotImplementedException();
            }
 
            bool ICollection<KeyValuePair<stringobject>>.Contains(KeyValuePair<stringobject> item)
            {
 
                throw new NotImplementedException();
 
            }
 
            void ICollection<KeyValuePair<stringobject>>.CopyTo(KeyValuePair<stringobject>[] array, int arrayIndex)
            {
                throw new NotImplementedException();
 
            }
 
            int ICollection<KeyValuePair<stringobject>>.Count
            {
                get
                {
                    throw new NotImplementedException();
                }
            }
 
            bool ICollection<KeyValuePair<stringobject>>.IsReadOnly
            {
                get
                {
                    throw new NotImplementedException();
                }
            }
 
            bool ICollection<KeyValuePair<stringobject>>.Remove(KeyValuePair<stringobject> item)
            {
                throw new NotImplementedException();
            }
 
            IEnumerator<KeyValuePair<stringobject>> IEnumerable<KeyValuePair<stringobject>>.GetEnumerator()
            {
                throw new NotImplementedException();
            }
 
            IEnumerator IEnumerable.GetEnumerator()
            {
                throw new NotImplementedException();
            }
        }
    }

  

Asp.Net Core 轻松学-一行代码搞定文件上传 JSONHelper的更多相关文章

  1. Asp.Net Core 轻松学-一行代码搞定文件上传

    前言     在 Web 应用程序开发过程中,总是无法避免涉及到文件上传,这次我们来聊一聊怎么去实现一个简单方便可复用文件上传功能:通过创建自定义绑定模型来实现文件上传. 1. 实现自定义绑定模型 1 ...

  2. 【自动化专题】selenium如何轻松搞定文件上传

    使用selenium做自动化时,我们经常会遇到的一个让人头疼的问题就是文件上传. 问题的难点在于selenium无法识别并操作Windows窗口,若我们可以绕过弹出框直接把文件信息上传给选择按钮,难点 ...

  3. Asp.Net Core 轻松学系列-1阅读指引目录

    https://www.cnblogs.com/viter/p/10474091.html 目录 前言 1. 从安装到配置 2. 业务实现 3. 日志 4. 测试 5. 缓存使用 6.网络和通讯 7. ...

  4. WebAPI调用笔记 ASP.NET CORE 学习之自定义异常处理 MySQL数据库查询优化建议 .NET操作XML文件之泛型集合的序列化与反序列化 Asp.Net Core 轻松学-多线程之Task快速上手 Asp.Net Core 轻松学-多线程之Task(补充)

    WebAPI调用笔记   前言 即时通信项目中初次调用OA接口遇到了一些问题,因为本人从业后几乎一直做CS端项目,一个简单的WebAPI调用居然浪费了不少时间,特此记录. 接口描述 首先说明一下,基于 ...

  5. Asp.Net Core 轻松学-使用MariaDB/MySql/PostgreSQL和支持多个上下文对象

    前言 在上一篇文章中(Asp.Net Core 轻松学-10分钟使用EFCore连接MSSQL数据库)[https://www.cnblogs.com/viter/p/10243577.html],介 ...

  6. Asp.Net Core 轻松学-多线程之Task(补充)

    前言     在上一章 Asp.Net Core 轻松学-多线程之Task快速上手 文章中,介绍了使用Task的各种常用场景,但是感觉有部分内容还没有完善,在这里补充一下. 1. 任务的等待 在使用 ...

  7. Asp.Net Core 轻松学-利用文件监视进行快速测试开发

    前言     在进行 Asp.Net Core 应用程序开发过程中,通常的做法是先把业务代码开发完成,然后建立单元测试,最后进入本地系统集成测试:在这个过程中,程序员的大部分时间几乎都花费在开发.运行 ...

  8. 如何从40亿整数中找到不存在的一个 webservice Asp.Net Core 轻松学-10分钟使用EFCore连接MSSQL数据库 WPF实战案例-打印 RabbitMQ与.net core(五) topic类型 与 headers类型 的Exchange

    如何从40亿整数中找到不存在的一个 前言 给定一个最多包含40亿个随机排列的32位的顺序整数的顺序文件,找出一个不在文件中的32位整数.(在文件中至少确实一个这样的数-为什么?).在具有足够内存的情况 ...

  9. C# 中一些类关系的判定方法 C#中关于增强类功能的几种方式 Asp.Net Core 轻松学-多线程之取消令牌

    1.  IsAssignableFrom实例方法 判断一个类或者接口是否继承自另一个指定的类或者接口. public interface IAnimal { } public interface ID ...

随机推荐

  1. C# 实现PNG文件的背景透明显示,解决动态显示闪烁问题 【转】

    http://blog.sina.com.cn/s/blog_402c071e0102x4rl.html    以下内容,对于想要使用C#实现PNG图片背景透明显示,同时动态显示时无闪烁问题的人来说, ...

  2. Iterator 迭代器模式 MD

    迭代器模式 简介 Iterator模式是行为模式之一,它把对容器中包含的内部对象的访问[委让]给外部类,使用Iterator按顺序进行遍历访问. 在程序设计中,经常有这种情况:需要从大量的数据集合中一 ...

  3. 使用C#创建及调用WCF完整实例 (Windows服务宿主)

    关于WCF的概念.原理.优缺点等,在这里就不多说了,网上很多,可以自行搜索,比我解释的要专业的多. 这里直接说使用Windows 服务(Windows Service)作为宿主如何实现,其它方式不在此 ...

  4. Terminix:基于 GTK3 的平铺式 Linux 终端模拟器

    现在,你可以很容易的找到大量的 Linux 终端模拟器,每一个都可以给用户留下深刻的印象.但是,很多时候,我们会很难根据我们的喜好来找到一款心仪的日常使用的终端模拟器.这篇文章中,我们将会推荐一款叫做 ...

  5. DockPanel 类

    DockPanel 类 .NET Framework 4.5   其他版本   此主题尚未评级 - 评价此主题   定义您可水平或垂直排列子元素的区域,互相. 继承层次结构   System.Obje ...

  6. [HTML5] Avoiding CSS Conflicts via Shadow DOM CSS encapsulation

    Shadow DOM is part of the web components specification. It allows us to ship self contained componen ...

  7. 【PHP 】 伪静态 - 3. 伪静态的基本使用

    原理图: 原先浏览器输入的网址会发送到apache服务器,然后apache会调用php模块来处理,最后找到你所想访问的页面; 如果在apahce, httpd.conf文件中开启rewrite机制,则 ...

  8. Linux命令行和Shell高效率使用方法

    Ctrl+R快速搜索history Ctrl+P显示上一条命令 快速执行一条history命令:!!/!-number ======================================== ...

  9. .net反编译工具ILSpy

    下载地址:http://www.fishlee.net/service/softarchive/57

  10. CheeseZH: Stanford University: Machine Learning Ex1:Linear Regression

    (1) How to comput the Cost function in Univirate/Multivariate Linear Regression; (2) How to comput t ...