相关文章推荐
直爽的围巾  ·  JSON_VALUE ...·  6 月前    · 
腼腆的黑框眼镜  ·  java - Behaviour of ...·  1 年前    · 
伤情的莴苣  ·  No statements may be ...·  1 年前    · 
慈祥的紫菜汤  ·  Java ...·  1 年前    · 

最近要使用ASP.NET CORE WEBAPI用来下载文件,使用的.NET CORE 3.1。考虑如下场景:

  1. 文件是程序生成的。

  2. 文件应该能兼容各种格式。

  3. 浏览器可以感知进行下载。

经过简单的调研,得到以下结论。

  • ASP.NET CORE 提供FileResult这种类型的ActionResult,可以直接返回文件结果,不需要直接处理HttpResponse。

  • 通过Stream可以直接返回文件流供浏览器下载。

  • FileStreamResult是FileResult的具体实现,返回值应该是此类对象。

  • Stream有多种类型,适合直接内存中生成文件对象的是MemoryStream。
    对目标有了基础的了解,就可以开始动手实现了。

建立好ASP.NET CORE WEBAPI工程,把生成文件的代码独立出来一个函数。我这里需要是下载一个CSV格式的文件,因此生成一个CSV文件。
对于磁盘上的文件,可以使用FileStream对象,由于我这里需要运行中生成这个文件,需要使用MemoryStream。

using var stream = new MemoryStream();
using var writer = new StreamWriter(stream);
//生成标题
var propCollection = ttype.GetProperties();
foreach (var n in propCollection)
    writer.Write(n.Name);
    writer.Write(",");
writer.WriteLine();
//生成内容
foreach (var item in res)
    foreach (var n in propCollection)
        writer.Write(Convert.ToString(n.GetValue(item)));
        writer.Write(",");
    writer.WriteLine();
  
  1. 请不要考虑里面反射的相关内容,按照自己的逻辑生成CSV即可,我只是懒得改代码而已。

  2. 代码中使用到了一些新的语法特性,请注意对低版本的.NET不一定适用。
    直接返回Stream对象给Controller处理,处理代码如下:

var res = await info.GetAllQueryResult();
var actionresult = new FileStreamResult(res, new Microsoft.Net.Http.Headers.MediaTypeHeaderValue("text/csv"));
return actionresult;
  

CSV的Content-Type是text/csv,如果下载别的文件,请自行查询MIME格式。

直接执行上面的代码,直接报错“无法读取已经关闭的流”。猜测是离开using语句块的时候,stream自动被关闭了。改动很简单,去掉using语句,不再报相同错误。

但是返回的文件长度一直是0,单步调试发现Writer执行完毕之后,stream返回的长度是0,内容实际上并没有写入,想起有一个Flush(),可以添加以确保数据写入。

单步显示stream长度有了,但是返回的长度还是0。继续单步调试发现Stream的Postion是停在文件结尾的,这个和直接开始读取文件完全不一样,文件读取一般是从开头开始的,于是直接设置Postion为0,问题解决。

下载能够成功了,但是文件名一直显示的是随机生成的,体验很差。设置一下FileDownloadName即可。

核心代码如下:

public async Task<Stream> GetAllQueryResult()
    var stream = new MemoryStream();
    var writer = new StreamWriter(stream);
    //生成标题
    var propCollection = ttype.GetProperties();
    foreach (var n in propCollection)
        writer.Write(n.Name);
        writer.Write(",");
    writer.WriteLine();
    //生成内容
    foreach (var item in res)
        foreach (var n in propCollection)
            writer.Write(Convert.ToString(n.GetValue(item)));
            writer.Write(",");
        writer.WriteLine();
    writer.Flush();
    stream.Position = 0;
    return stream;
 
[HttpPost("file")]
[ProducesResponseType(typeof(FileResult), Status200OK)]
public async Task<FileResult> Download()
    var info = new Info();
    var res = await info.GetAllQueryResult();
    var actionresult = new FileStreamResult(res, new Microsoft.Net.Http.Headers.MediaTypeHeaderValue("text/csv"));
    actionresult.FileDownloadName = "Carinfos.csv";
    //Response.ContentLength = res.Length;
    return actionresult;
 

使用swagger调用,最后效果:

后来查了一些资料,总结了一下:

  • MemoryStream如果使用using语句,会在离开代码块的时候自动关闭,实际上ASP.NET CORE会自动处理关闭的事项,不需要使用using语句。

  • 由于生成文件的过程是从文件流的开头一直进行到末尾的,因此向请求端返回结果时,应当重置Stream的游标,从0开始传输。

  • 记得在使用writer之后使用Flush()以确保数据有写入。

  • 如果不确定文件格式,可以直接返回MIME值为application/oct-stream。

  • 设置FileStreamResult的FileDownloadName属性可以修改文件的默认名称。

  • (可选)可以通过设置Response.ContentLength来设置文件的长度。

参考资料:

https://darchuk.net/2019/05/31/asp-net-core-web-api-returning-a-filestream/

最近要使用ASP.NET CORE WEBAPI用来下载文件,使用的.NET CORE 3.1。考虑如下场景:文件是程序生成的。文件应该能兼容各种格式。浏览器可以感知进行下载。准备经过...
下载文件到本地是很多项目开发中需要实现的一个很简单的功能。说简单,是从具体的代码实现上来说的,.NET的文件下载方式有很多种,本示例给大家介绍的是ASP.NET Web Api方式返回HttpResponseMessage下载文件到本地。实现的方法很简单,其中就是读取服务器的指定路径文件流,将其做为返回的HttpResponseMessage的Content。直接贴出DownloadController控件器的代码: using System; using System.Collections.Generic; using System.IO; using System.Linq;
ASP.NET Web API实现简单的文件下载与上传。首先创建一个ASP.NET Web API项目,然后在项目下创建FileRoot目录并在该目录下创建ReportTemplate.xlsx文件,用于下面示例的使用。 1、文件下载 示例:实现报表模板文件下载功能。 1.1 后端代码 /// &lt;summary&gt; /// 下载文件 /// &lt;/summary&gt; 你要明白,任何问题都不是孤立存在的,一定有人曾经遇到过,并且已经有更好的解决办法了,只是我还不知道。我不应该在黑暗中独自前行,去重新发明轮子,也许我的顿悟,只是别人的基本功!我应该要站在巨人的肩膀上,学习更成熟的经验和方法,然后再来解决这个问题 02-21 使用起来也很简单,我们构造 ExcelHelper 类,并在controller里面使用。 比如 person类有 id,name,age 3个属性,则 在controller里面这样调用 [Route("ExportExcel")] [HttpGet] public IActionRes...
publicasyncTask<HttpResponseMessage>DownloadAsync(stringid) varresponse=newHttpResponseMessage(HttpStatusCode.OK); response.Content=newStreamCo...
本篇为大家介绍WebApi又一个必不可少的功能,那就是文件上传下载。 还记得我们在初期改造项目的时候删掉的wwwroot文件夹吗,这里放的就是项目中的静态资源文件,接下来我们来手动实现这个功能。 1、我们为项目添加一个静态的工具类,命名为CommonFun,并添加如下代码(以后你所有的静态扩展方法都可以放到这里) using System.Text; namespace NET6.Infrastructure.Tools /// <summary> /// 工具类