1
以数据流形式渲染显示头像图片
1.1 Core.
MimeTypes
namespace
Core
{
///
<summary>
///
【
Mime
类型
—
类】
///
<remarks>
///
摘要:
///
媒体类型(通常称为
Multipurpose Internet Mail Extensions
或
MIME
类型)是一种标准,用来表示文档、文件或字节流的性质和格式。
///
它在
IETF RFC 6838
中进行了定义和标准化。
///
警告
/
说明:
/// 1
、浏览器通常使用
MIME
类型(而不是文件扩展名)来确定如何处理
URL
,因此
Web
服务器在响应头中添加正确的
MIME
类型非常重要。
///
如果配置不正确,浏览器可能会曲解文件内容,网站将无法正常工作,并且下载的文件也会被错误处理。
/// 2
、
MIME
类型主要是为浏览器通常以静态文件的形式渲染显示指定类型的持久化文件提供数据支撑,
///
如果先把指定类型的持久化文件转换为数据流再在浏览器渲染显示时,则不用,但是在持久化文件转换为数据流操作时必须为其提供相应的
MIME
类型作为数据支撑。
///
</remarks>
///
</summary>
public
static
class
MimeTypes
{
#
region
应用
///
<summary>
///
【应用程序强制下载】
///
<remarks>
///
摘要:
///
客户端浏览器头字典实例中设定指定静态文件为下载文件。
///
</remarks>
///
</summary>
public
static
string
ApplicationForceDownload
=>
“application/force-download”
;
///
<summary>
///
【应用程序
Json
】
///
<remarks>
///
摘要:
///
客户端浏览器头字典实例中设定服务器端发送的指定文件为以
Json
编码格式进行持久化存储的静态文件,同时客户端浏览器也会以
Json
编码格式渲染显示该静态文件。
///
</remarks>
///
</summary>
public
static
string
ApplicationJson
=>
“application/json”
;
///
<summary>
///
【应用程序清单】
///
<remarks>
///
摘要:
///
客户端浏览器头字典实例中设定服务器端发送的指定文件为以
Json/XML
编码格式进行持久化存储的静态文件,同时客户端浏览器也会以
Json/XML
编码格式渲染显示该静态文件。
///
</remarks>
public
static
string
ApplicationManifestJson
=>
“application/manifest+json”
;
///
<summary>
///
【应用程序八位字节流】
///
<remarks>
///
摘要:
///
客户端浏览器向服务器端上传持久化存储静态文件时,只能以二进制数据流(或者字节数组
)
进行上传,且以此标准一次只能向服务器端上传一个静态文件
,
服务器端的接收参数也只能有一个。
///
</remarks>
///
</summary>
public
static
string
ApplicationOctetStream
=>
“application/octet-stream”
;
///
<summary>
///
【应用程序
PDF
】
///
<remarks>
///
摘要:
///
客户端浏览器头字典实例中设定服务器端发送的指定文件为以
PDF
编码格式进行持久化存储的静态文件,同时客户端浏览器也会以
PDF
编码格式渲染显示该静态文件。
///
</remarks>
///
</summary>
public
static
string
ApplicationPdf
=>
“application/pdf”
;
///
<summary>
///
【应用程序
RSSXML
】
///
<remarks>
///
摘要:
/// 1
、据说搜索引擎也会根据这个标签自动去爬行博客的
RSS
;
/// 2
、让
Firefox
、
IE7
或其它
Feed
机器人自动发现网站的
RSS
视图;
///
</remarks>
///
</summary>
public
static
string
ApplicationRssXml
=>
“application/rss+xml”
;
///
<summary>
///
【应用程序
XML
】
///
<remarks>
///
摘要:
///
客户端浏览器头字典实例中设定服务器端发送的指定文件为以
XML
编码格式进行持久化存储的静态文件,同时客户端浏览器也会以
XML
编码格式渲染显示该静态文件。
///
</remarks>
public
static
string
ApplicationXml
=>
“application/xml”
;
///
<summary>
///
【窗体应用程序】
///
<remarks>
///
摘要:
///
客户端浏览器以键
/
值对的形式把地址栏中的参数值发送到服务器端的指定的控制器行为方法。
///
说明:
/// Postman
以此标准把键
/
值对发送到服务器端的指定的控制器行为方法。
///
</remarks>
///
</summary>
public
static
string
ApplicationXWwwFormUrlencoded
=>
“application/x-www-form-urlencoded”
;
///
<summary>
///
【应用程序
Zip
】
///
<remarks>
///
摘要:
///
客户端浏览器头字典实例中设定服务器端发送的指定文件为静态压缩文件,同时客户端浏览器也会以此标准打开静态压缩文件。
///
</remarks>
///
</summary>
public
static
string
ApplicationZip
=>
“application/zip”
;
///
<summary>
///
【应用程序
Zip
扩展】
///
<remarks>
///
摘要:
///
客户端浏览器头字典实例中设定服务器端发送的指定文件为静态压缩文件,同时客户端浏览器也会以此标准打开静态压缩文件。
///
</remarks>
///
</summary>
public
static
string
ApplicationXZipCo
=>
“application/x-zip-co”
;
#
endregion
#
region
图片
///
<summary>
///
【
Bmp
图片】
///
<remarks>
///
摘要:
///
客户端浏览器头字典实例中设定服务器端发送的指定文件以
“bmp”
为扩展名的静态图片文件,同时客户端浏览器也会以此标准打开该静态图片文件。
///
</remarks>
///
</summary>
public
static
string
ImageBmp
=>
“image/bmp”
;
///
<summary>
///
【
Gif
图片】
///
<remarks>
///
摘要:
///
客户端浏览器头字典实例中设定服务器端发送的指定文件以
“Gif”
为扩展名的静态图片文件,同时客户端浏览器也会以此标准打开该静态图片文件。
///
</remarks>
///
</summary>
public
static
string
ImageGif
=>
“image/gif”
;
///
<summary>
///
【
Jpeg
图片】
///
<remarks>
///
摘要:
///
客户端浏览器头字典实例中设定服务器端发送的指定文件以
“jpe/jpeg/jpg/jfif/pjpeg/pjp”
为扩展名的静态图片文件,同时客户端浏览器也会以此标准打开该静态图片文件。
///
</remarks>
///
</summary>
public
static
string
ImageJpeg
=>
“image/jpeg”
;
///
<summary>
///
【
pjpeg
图片】
///
<remarks>
///
摘要:
///
客户端浏览器头字典实例中设定服务器端发送的指定文件以
“jpe/jpeg/jpg/jfif/pjpeg/pjp”
为扩展名的静态图片文件,同时客户端浏览器也会以此标准打开该静态图片文件。
///
</remarks>
///
</summary>
public
static
string
ImagePJpeg
=>
“image/pjpeg”
;
///
<summary>
///
【
Png
图片】
///
<remarks>
///
摘要:
///
客户端浏览器头字典实例中设定服务器端发送的指定文件以
“Png”
为扩展名的静态图片文件,同时客户端浏览器也会以此标准打开该静态图片文件。
///
</remarks>
///
</summary>
public
static
string
ImagePng
=>
“image/png”
;
///
<summary>
///
【
Tiff
图片】
///
<remarks>
///
摘要:
///
客户端浏览器头字典实例中设定服务器端发送的指定文件以
“tiff”
为扩展名的静态图片文件,同时客户端浏览器也会以此标准打开该静态图片文件。
///
</remarks>
///
</summary>
public
static
string
ImageTiff
=>
“image/tiff”
;
///
<summary>
///
【
Webp
图片】
///
<remarks>
///
摘要:
///
客户端浏览器头字典实例中设定服务器端发送的指定文件以
“webp”
为扩展名的静态图片文件,同时客户端浏览器也会以此标准打开该静态图片文件。
///
</remarks>
///
</summary>
public
static
string
ImageWebp
=>
“image/webp”
;
///
<summary>
///
【
Svg
图片】
///
<remarks>
///
摘要:
///
客户端浏览器头字典实例中设定服务器端发送的指定文件以
“svg”
为扩展名的静态图片文件,同时客户端浏览器也会以此标准打开该静态图片文件。
///
</remarks>
///
</summary>
public
static
string
ImageSvg
=>
“image/svg+xml”
;
#
endregion
#
region
文本
///
<summary>
///
【
CSS
文本】
///
<remarks>
///
摘要:
///
客户端浏览器头字典实例中设定服务器端发送的指定文件为以
CSS
编码格式进行持久化存储的静态文本文件,同时客户端浏览器也会以
CSS
编码格式渲染显示该静态文本文件。
///
</remarks>
///
</summary>
public
static
string
TextCss
=>
“text/css”
;
///
<summary>
///
【】
///
<remarks>
///
摘要:
///
客户端浏览器头字典实例中设定服务器端发送的指定文件为以
Csv
码格式进行持久化存储的静态文本文件,同时客户端浏览器也会以
Csv
编码格式渲染显示该静态
Csv
文本文件。
///
</remarks>
///
</summary>
public
static
string
TextCsv
=>
“text/csv”
;
///
<summary>
///
【
Javascript
文本】
///
<remarks>
///
摘要:
///
客户端浏览器头字典实例中设定服务器端发送的指定文件为以
Javascript
编码格式进行持久化存储的静态文本文件,同时客户端浏览器也会以
Javascript
编码格式渲染显示该静态
Javascript
文本文件。
///
</remarks>
///
</summary>
public
static
string
TextJavascript
=>
“text/javascript”
;
///
<summary>
///
【纯文本
(text)
】
///
<remarks>
///
摘要:
///
客户端浏览器头字典实例中设定服务器端发送的指定文件为以纯文本
(text)
编码格式进行持久化存储的静态文本文件,同时客户端浏览器也会以纯文本
(text)
编码格式渲染显示该静态纯文本
(text)
文件。
///
</remarks>
///
</summary>
public
static
string
TextPlain
=>
“text/plain”
;
///
<summary>
///
【
Excel
文本】
///
<remarks>
///
摘要:
///
客户端浏览器头字典实例中设定服务器端发送的指定文件为以
Excel
编码格式进行持久化存储的静态文本文件,同时客户端浏览器也会以
Excel
编码格式渲染显示该静态
Excel
文本文件。
///
</remarks>
///
</summary>
public
static
string
TextXlsx
=>
“application/vnd.openxmlformats-officedocument.spreadsheetml.sheet”
;
#
endregion
}
}
1.2 WebApi.Controllers.
CustomerController
.
GetPictureContentTypeByFileExtension
///
<param
name
=
“fileExtension”
>
1
个指定图片的扩展名。
</param>
///
<summary>
///
【通过扩展名获取图片类型】
///
</summary>
///
<remarks>
///
摘要:
///
通过
1
个指定图片的扩展名获取
1
个指定图片的类型。
///
</remarks>
///
<returns>
///
返回:
/// 1
个指定图片的类型。
///
</returns>
private
string
GetPictureContentTypeByFileExtension
(
string
fileExtension
)
{
string
contentType
=
null
;
switch
(
fileExtension
.
ToLower
())
{
case
“.bmp”
:
contentType
=
MimeTypes
.
ImageBmp
;
break
;
case
“.gif”
:
contentType
=
MimeTypes
.
ImageGif
;
break
;
case
“.jpeg”
:
case
“.jpg”
:
case
“.jpe”
:
case
“.jfif”
:
case
“.pjpeg”
:
case
“.pjp”
:
contentType
=
MimeTypes
.
ImageJpeg
;
break
;
case
“.webp”
:
contentType
=
MimeTypes
.
ImageWebp
;
break
;
case
“.png”
:
contentType
=
MimeTypes
.
ImagePng
;
break
;
case
“.svg”
:
contentType
=
MimeTypes
.
ImageSvg
;
break
;
case
“.tiff”
:
case
“.tif”
:
contentType
=
MimeTypes
.
ImageTiff
;
break
;
default
:
break
;
}
return
contentType
;
}
1.3 WebApi.Controllers.
CustomerController
.
GetAvatarStream
///
<param
name
=
“customerId”
>
1
个指定的长整型值。
</param>
///
<summary>
///
【以数据流形式渲染显示头像图片
—
无需权限】
///
</summary>
///
<remarks>
///
摘要:
///
获取
1
个指定用户头像图片的数据流,为前端头像图片的渲染显示提供数据支撑。
///
</remarks>
///
<returns>
///
返回:
/// 1
个指定用户头像图片的数据流,为前端头像图片的渲染显示提供数据支撑。
///
</returns>
[
HttpGet
]
public
async
Task
<
IActionResult
>
GetAvatarStream
(
long
customerId
)
{
Customer
_customer
=
await
_customerService
.
GetCustomerByIdAsync
(
customerId
);
string
_avatarPath
=
string
.
Empty
;
if
(
_customer
!=
null
)
{
if
(
string
.
IsNullOrEmpty
(
_customer
.
Avatar
))
{
_avatarPath
=
_nopFileProvider
.
Combine
(
_nopFileProvider
.
WebRootPath
,
@”\images\Avatar\Default.jpg”
);
}
else
{
//
去除网络格式路径字符中的第一个字符
“~/”
。
_customer
.
Avatar
=
_customer
.
Avatar
.
Replace
(
“~/”
,
string
.
Empty
).
TrimStart
(‘
/
‘);
//
去除网络格式路径字符中的最后一个字符
“/”
。
var
pathEnd
=
_customer
.
Avatar
.
EndsWith
(‘
/
‘)
?
Path
.
DirectorySeparatorChar
.
ToString
()
:
string
.
Empty
;
//
通过拼接操作,拼接出与之相对应的
1
个本地格式的路径字符串。
_avatarPath
=
_nopFileProvider
.
Combine
(
_nopFileProvider
.
WebRootPath
??
string
.
Empty
,
_customer
.
Avatar
)
+
pathEnd
;
}
}
string
_avatarType
=
GetPictureContentTypeByFileExtension
(
_nopFileProvider
.
GetFileExtension
(
_avatarPath
));
var
imageFileStream
=
System
.
IO
.
File
.
OpenRead
(
_avatarPath
);
return
File
(
imageFileStream
,
_avatarType
);
}
2 Swagger
通过IFormFile或IFormCollection参数实例实现文件上传
2.1 415
异常:
Swagger/index.html
在默认情况下是可以通过IFormFile或IFormCollection参数实例向服务器端上传控制器行为方法发送参数实例的实现文件上传功能的,但是由于Framework.Infrastructure.Middleware.
CorsExceptionHandlerMiddleware
中间件定义会出现415异常,
如下图上所示:
2.2
解决方案:重构
Framework.Infrastructure.Middleware.
CorsExceptionHandlerMiddleware
using
Microsoft
.
AspNetCore
.
Cors
.
Infrastructure
;
using
Microsoft
.
AspNetCore
.
Http
;
namespace
Framework
.
Infrastructure
.
Middleware
{
///
<summary>
///
【跨域异常处理中间件
—
类】
///
<remarks>
///
摘要:
///
该管道中间件类主要为了集中解决在由
vue/uni-app
前端项目跨域
(Cors)
访问当前后端项目时,浏览器或
App
中出现的异常:
/// 1
、
“has been blocked by CORS policy: Request header field content-type is not allowed by Access-Control-Allow-Headers in preflight response.”
。
/// 2
、
“has been blocked by CORS policy: No ‘Access-Control-Allow-Origin’ header is present on the requested resource.”
。
/// 3
、
“has been blocked by CORS policy: Response to preflight request doesn’t pass access control check: It does not have HTTP ok status.”
。
///
说明:
/// vue/uni-app
前端项目跨域
(Cors)
访问当前后端项目时,浏览器或
App
中出现的异常,可以直接在
vue/uni-app
前端项目中解决;但本人更习惯通过后端定义来集中解决这些异常。
///
</remarks>
///
</summary>
public
class
CorsExceptionHandlerMiddleware
{
#region
拷贝构造方法与变量
///
<summary>
///
【下
1
个】
///
<remarks>
///
摘要:
/// .Net(Core)
框架内置管道中的下
1
个管道中间件实例。
///
</remarks>
///
</summary>
private
readonly
RequestDelegate
_next
;
///
<param
name
=
“next”
>
.Net(Core)
框架内置管道中的下
1
个管道中间件实例。
</param>
///
<summary>
///
【拷贝构造方法】
///
<remarks>
///
摘要:
///
通过该构造方法中的参数实例,实例化
.Net(Core)
框架内置管道中的下
1
个管道中间件实例。
///
</remarks>
///
</summary>
public
CorsExceptionHandlerMiddleware
(
RequestDelegate
next
)
{
_next
=
next
;
}
#endregion
#region
方法
///
<param
name
=
“context”
>
HTTP
上下文实例。
</param>
///
<summary>
///
【异步调用】
///
<remarks>
///
摘要:
///
通过该方法向
.Net(Core)
框架内置管道中集成当前管道中间件,集中解决在由
vue/uni-app
前端项目跨域
(Cors)
访问当前后端项目时,浏览器或
App
中出现的异常:
/// 1
、
“has been blocked by CORS policy: Request header field content-type is not allowed by Access-Control-Allow-Headers in preflight response.”
。
/// 2
、
“has been blocked by CORS policy: No ‘Access-Control-Allow-Origin’ header is present on the requested resource.”
。
/// 3
、
“has been blocked by CORS policy: Response to preflight request doesn’t pass access control check: It does not have HTTP ok status.”
。
///
</remarks>
///
</summary>
public
async
Task
InvokeAsync
(
HttpContext
context
)
{
//
解决在由
Hbuilder
创建的前端
Xuni-app
项目
(Cors)
访问当前后端项目时,浏览器或
App
中会出现异常:
//“has been blocked by CORS policy: Request header field content-type is not allowed by Access-Control-Allow-Headers in preflight response.”
。
if
(!
context
.
Response
.
Headers
.
ContainsKey
(
“Access-Control-Allow-Headers”
))
{
context
.
Response
.
Headers
.
Add
(
“Access-Control-Allow-Headers”
,
“DNT,X-Mx-ReqToken,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization”
);
}
if
(!
context
.
Response
.
Headers
.
ContainsKey
(
“Access-Control-Allow-Methods”
))
{
context
.
Response
.
Headers
.
Add
(
“Access-Control-Allow-Methods”
,
“GET,POST,PUT,DELETE,PATCH,OPTIONS”
);
}
//
解决在由
Hbuilder
创建的前端
Xuni-app
项目
(Cors)
访问当前后端项目时,浏览器或
App
中会出现异常:
//“has been blocked by CORS policy: No ‘Access-Control-Allow-Origin’ header is present on the requested resource.”
。
if
(!
context
.
Response
.
Headers
.
ContainsKey
(
“Access-Control-Allow-Origin”
))
{
context
.
Response
.
Headers
.
Add
(
“Access-Control-Allow-Origin”
,
“*”
);
}
if
(
context
.
Request
.
Headers
.
ContainsKey
(
CorsConstants
.
Origin
))
{
//
解决在前端通过
“axios.post”
方式调用后端
POST-API
有
,
如果前端
“axios.post”
方法没有加载
“headers”
参数实例,下
1
行语句中的配置,否则
“axios.post”
方法,访问后端的
POST-API,
否则会出现
:”HTTP:415″
错误。
//context.Request.ContentType = “application/json”;
if
(!
context
.
Request
.
ContentType
.
Contains
(
“multipart/form-data”
))
context
.
Request
.
ContentType
=
“application/json”
;
//
解决在由
Hbuilder
创建的前端
Xuni-app
项目
(Cors)
访问当前后端项目时,浏览器或
App
中会出现异常:
//“’ has been blocked by CORS policy: Response to preflight request doesn’t pass access control check: It does not have HTTP ok status.”
。
if
(
context
.
Request
.
Method
.
Equals
(
“OPTIONS”
))
{
context
.
Response
.
StatusCode
=
StatusCodes
.
Status200OK
;
return
;
}
}
await
_next
(
context
);
}
#endregion
}
}
2.3 WebApi.Controllers.
CustomerController
.
PostAvatarStream
///
<param
name
=
“customerId”
>
1
个指定的长整型值。
</param>
///
<param
name
=
“formFile”
>
1
个指定的上传文件的实例。
</param>
///
<summary>
///
【上传单个文件
—
无需权限】
///
</summary>
///
<remarks>
///
摘要:
///
把
1
个指定的上传文件从客户端上传到服务器端的指定目录中。
///
</remarks>
///
<returns>
///
返回:
/// 1
个指定的上传文件上传操作后的状态信息。
///
</returns>
[
HttpPost
]
public
async
Task
<
IActionResult
>
PostAvatarStream
(
long
customerId
,
/*IFormCollection collection [FromForm]*/
IFormFile
formFile
)
{
Customer
_customer
=
await
_customerService
.
GetCustomerByIdAsync
(
customerId
);
//string _avatarPath = string.Empty;
if
(
_customer
!=
null
&&
formFile
!=
null
)
{
if
(!
string
.
IsNullOrEmpty
(
_customer
.
Avatar
)&&!
_nopFileProvider
.
GetFileName
(
_customer
.
Avatar
).
Equals
(
“Default.jpg”
))
{
//
去除网络格式路径字符中的第一个字符
“~/”
。
_customer
.
Avatar
=
_customer
.
Avatar
.
Replace
(
“~/”
,
string
.
Empty
).
TrimStart
(
‘/’
);
//
去除网络格式路径字符中的最后一个字符
“/”
。
var
pathEnd
=
_customer
.
Avatar
.
EndsWith
(
‘/’
) ?
Path
.
DirectorySeparatorChar
.
ToString
() :
string
.
Empty
;
//
通过拼接操作,拼接出与之相对应的
1
个本地格式的路径字符串。
string
_avatarPath
=
_nopFileProvider
.
Combine
(
_nopFileProvider
.
WebRootPath
??
string
.
Empty
,
_customer
.
Avatar
) +
pathEnd
;
_nopFileProvider
.
DeleteFile
(
_avatarPath
);
}
string
_filename
=
customerId
.
ToString
() +
_nopFileProvider
.
GetFileExtension
(
formFile
.
FileName
);
string
_path
=
_nopFileProvider
.
Combine
(
_nopFileProvider
.
WebRootPath
,
@”\images\Avatar\”
,
_filename
);
using
FileStream
fileStream
=
new
FileStream
(
_path
,
FileMode
.
Create
);
await
formFile
.
CopyToAsync
(
fileStream
);
_customer
.
Avatar
=
“/images/Avatar/”
+
_filename
;
await
_customerService
.
UpdateCustomerAsync
(
_customer
);
return
Created
(
WebUtility
.
UrlEncode
(
_customer
.
Avatar
),
_customer
);
}
return
BadRequest
();
}
对以上功能更为具体实现和注释见:230322_045shopDemo(Swagger通过IFormFile或IFormCollection参数实例实现文件上传)。