受欢迎的博客标签

ASP .Net Core-配置content-type文件下载类型

Published
.NetCore下载文件,常见的下载方式会用到两种:
1.A标签直接指向下载文件地址
2.post或get请求后台输出文件流的方式
本篇也将围绕这两种情况说明
 
  • 允许站点不识别content-type下载文件(即:不受mime类型限制下载)
  • 如何允许下载.nupkg和.apk后缀的文件
  • Razor模板的post下载文件例子
  • Default MIME type(.Net 6.x )
 
 
 
1.允许站点不识别content-type下载文件(即:不受mime类型限制下载)

对于netcore的web项目而言,内置了一些content-type允许下载的文件类型;我们将通过一个普通的razorweb项目来看看直接通过连接下载excel例子;首先,在项目的wwwroot目录创建一个bak文件夹,然后在该目录下存放如下几种文件:

然后不用修改任何代码或设置,直接启动站点,再直接在浏览器地址栏分别录入下载文件地址,如:

http://localhost:1120/bak/excel.xls
http://localhost:1120/bak/love.apk
http://localhost:1120/bak/stackexchange.redis.1.2.6.nupkg
http://localhost:1120/bak/Startup.cs

通过测试这个时候只有excel.xls文件是能直接被下载的,其他的都是404:

.Net 5.x and older 

要想.apk,.nupkg.cs等后缀的文件不被限制,我们可以通过 

public static IApplicationBuilder UseStaticFiles(this IApplicationBuilder app, StaticFileOptions options); 

扩展来设置,我们仅仅只需要修改为如下代码:

app.UseStaticFiles(
new StaticFileOptions { 
//设置不限制content-type 
ServeUnknownFileTypes = true
 });

然后在重启运行,这个时候我们再来访问下载这几个文件就没问题了(注意这个时候下载任何后缀的文件都行),如下截图:

至于cs后缀的文件在google浏览器中是直接显示的内容,这里就不贴图了,有兴趣的可以试试;

如何允许下载.nupkg和.apk后缀的文件

通过上面例子我们能够使用 ServeUnknownFileTypes = true; 直接设置无限制下载文件类型,这种通常不是太好或者说不允许,亦或者不常说的不安全吧;如果我们只需要增加.nupkg和.apk后缀的文件的下载,那么可以通过如下代码来添加mime类型,如:

.Net 6.x

https://docs.microsoft.com/en-us/aspnet/core/fundamentals/static-files?view=aspnetcore-6.0#fileextensioncontenttypeprovider

Namespace:Microsoft.AspNetCore.StaticFiles

public class FileExtensionContentTypeProvider : Microsoft.AspNetCore.StaticFiles.IContentTypeProvider

 

FileExtensionContentTypeProvider()  Provides a mapping between file extensions and MIME types.

using Microsoft.AspNetCore.StaticFiles;

//come from:https://docs.microsoft.com/en-us/aspnet/core/fundamentals/static-files?view=aspnetcore-6.0#fileextensioncontenttypeprovider

//FileExtensionContentTypeProvider() Provides a mapping between file extensions and MIME types.
//Set up custom content types - associating file extension to MIME type
var provider = new FileExtensionContentTypeProvider();
// Add new mappings
provider.Mappings[".apk"] = "application/vnd.android.package-archive";


app.UseStaticFiles(new StaticFileOptions
{
    ContentTypeProvider = provider
});

 

.Net 5.x

public class FileExtensionContentTypeProvider : Microsoft.AspNetCore.StaticFiles.IContentTypeProvider
 
app.UseStaticFiles(new StaticFileOptions
            {
                //ServeUnknownFileTypes = true 
                ContentTypeProvider = new FileExtensionContentTypeProvider(new Dictionary<string, string>
                {
                    { ".apk","application/vnd.android.package-archive"},
                    { ".nupkg","application/zip"}
                })
            });


同样的也能对excel,apk,nupkg后缀的文件进行下载:

但是这个时候我们访问 http://localhost:1120/bak/Startup.cs 就得不到下载的内容了:

因为我们没有添加对.cs文件的扩展类型,故而系统直接给咋们返回404;这里我们通过FileExtensionContentTypeProvider对象的构造函数传递了一个mapping的dic类型来让项目知道允许下载的content-type类型的文件;

2.Razor模板的post下载文件例子

老实说最近一端时间有空我就会研究下Razor模板,下面我们将通过她的post表单的方式来请求后端下载文件的方法;下面直接给出login.cshtml文件的代码:

 
@page
@model LoginModel
@{}
<form method="post">
    <button type="submit" asp-page-handler="down" class="btn">下载</button>
    <button type="submit" asp-page-handler="down01" class="btn">下载01</button>
    <button type="submit" asp-page-handler="down02" class="btn">下载02</button>
</form>
 

这里值得注意的是,razor通过asp-page-handler=来执行请求后端的方法,我们来看看最终她生成的html代码后是什么样子的:

能够看出这里主要通过handler作为参数名称来传递请求的后端方法,下面再来看看后端代码这样写的(为了方便下载文件的路劲我以love.apk为例):

 
/// <summary>
        /// 虚拟文件地址输出下载
        /// </summary>
        /// <returns></returns>
        public IActionResult OnPostDown()
        {
            var addrUrl = "/bak/love.apk";
            return File(addrUrl, "application/vnd.android.package-archive", Path.GetFileName(addrUrl));
        }
        
        /// <summary>
        /// 文件流的方式输出
        /// </summary>
        /// <returns></returns>
        public IActionResult OnPostDown01()
        {
            var addrUrl = @"D:\F\学习\vs2017\netcore\Study.AspNetCore\WebApp02-1\wwwroot\bak\love.apk";
            var stream = System.IO.File.OpenRead(addrUrl);
            return File(stream, "application/vnd.android.package-archive", Path.GetFileName(addrUrl));
        }

        /// <summary>
        /// 通过HttpClient获取另外站点的文件流,再输出
        /// </summary>
        /// <returns></returns>
        public async Task<IActionResult> OnPostDown02()
        {
            var path = "https://files.cnblogs.com/files/wangrudong003/%E7%89%B9%E4%BB%B701.gif";
            HttpClient client = new HttpClient();
            client.BaseAddress = new Uri(path);
            var stream = await client.GetStreamAsync(path);
            return File(stream, "application/vnd.android.package-archive", Path.GetFileName(path));
        }
 

后端3个post接受方法都同样使用了FileStreamResult来输出下载文件,不同点在于文件来源不同;

对于简单一些的站点来说,下载文件一般存在于站点目录下,有点类似于我这里的wwwroot/bak目录,因此能够通过站点虚拟目录下载,也就是咋们第一种的下载方式;

有一些站点为了文件安全性,一般会存在于web站点的相同服务器磁盘中,因此需要通过这里的第二种方式获取文件流,再传递给File();

最后一种就是把自己其他站点或者别人站点上的文件转一下,当做自己的文件来输出,这种方式也就是咋们常说的盗链方式之一

对于razor的handler参数,这里需要注意的是她对应是咋们后端代码OnGetxxx或者OnPostxxx方法中的xxx名称,这是一种razor请求规范,必须要遵守哦。

 

.Net 6.x Default MIME type

 

namespace System.Net.Mime
{
    //
    // 摘要:
    //     Specifies the media type information for an email message attachment.
    public static class MediaTypeNames
    {
        //
        // 摘要:
        //     Specifies the kind of application data in an email message attachment.
        public static class Application
        {
            //
            // 摘要:
            //     Specifies that the System.Net.Mime.MediaTypeNames.Application data is in JSON
            //     format.
            public const string Json = "application/json";

            //
            // 摘要:
            //     Specifies that the System.Net.Mime.MediaTypeNames.Application data is not interpreted.
            public const string Octet = "application/octet-stream";

            //
            // 摘要:
            //     Specifies that the System.Net.Mime.MediaTypeNames.Application data is in Portable
            //     Document Format (PDF).
            public const string Pdf = "application/pdf";

            //
            // 摘要:
            //     Specifies that the System.Net.Mime.MediaTypeNames.Application data is in Rich
            //     Text Format (RTF).
            public const string Rtf = "application/rtf";

            //
            // 摘要:
            //     Specifies that the System.Net.Mime.MediaTypeNames.Application data is a SOAP
            //     document.
            public const string Soap = "application/soap+xml";

            //
            // 摘要:
            //     Specifies that the System.Net.Mime.MediaTypeNames.Application data is in XML
            //     format.
            public const string Xml = "application/xml";

            //
            // 摘要:
            //     Specifies that the System.Net.Mime.MediaTypeNames.Application data is compressed.
            public const string Zip = "application/zip";
        }

        //
        // 摘要:
        //     Specifies the type of image data in an email message attachment.
        public static class Image
        {
            //
            // 摘要:
            //     Specifies that the System.Net.Mime.MediaTypeNames.Image data is in Graphics Interchange
            //     Format (GIF).
            public const string Gif = "image/gif";

            //
            // 摘要:
            //     Specifies that the System.Net.Mime.MediaTypeNames.Image data is in Joint Photographic
            //     Experts Group (JPEG) format.
            public const string Jpeg = "image/jpeg";

            //
            // 摘要:
            //     Specifies that the System.Net.Mime.MediaTypeNames.Image data is in Tagged Image
            //     File Format (TIFF).
            public const string Tiff = "image/tiff";
        }

        //
        // 摘要:
        //     Specifies the type of text data in an email message attachment.
        public static class Text
        {
            //
            // 摘要:
            //     Specifies that the System.Net.Mime.MediaTypeNames.Text data is in HTML format.
            public const string Html = "text/html";

            //
            // 摘要:
            //     Specifies that the System.Net.Mime.MediaTypeNames.Text data is in plain text
            //     format.
            public const string Plain = "text/plain";

            //
            // 摘要:
            //     Specifies that the System.Net.Mime.MediaTypeNames.Text data is in Rich Text Format
            //     (RTF).
            public const string RichText = "text/richtext";

            //
            // 摘要:
            //     Specifies that the System.Net.Mime.MediaTypeNames.Text data is in XML format.
            public const string Xml = "text/xml";
        }
    }
}