最近要使用ASP.NET CORE WEBAPI用来下载文件,使用的.NET CORE 3.1。考虑以下场景:web
通过简单的调研,获得如下结论。c#
创建好ASP.NET CORE WEBAPI工程,把生成文件的代码独立出来一个函数。我这里须要是下载一个CSV格式的文件,所以生成一个CSV文件。
对于磁盘上的文件,可使用FileStream对象,因为我这里须要运行中生成这个文件,须要使用MemoryStream。api
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(); }
- 请不要考虑里面反射的相关内容,按照本身的逻辑生成CSV便可,我只是懒得改代码而已。
- 代码中使用到了一些新的语法特性,请注意对低版本的.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语句,再也不报相同错误。app
可是返回的文件长度一直是0,单步调试发现Writer执行完毕以后,stream返回的长度是0,内容实际上并无写入,想起有一个Flush(),能够添加以确保数据写入。async
单步显示stream长度有了,可是返回的长度仍是0。继续单步调试发现Stream的Postion是停在文件结尾的,这个和直接开始读取文件彻底不同,文件读取通常是从开头开始的,因而直接设置Postion为0,问题解决。函数
下载可以成功了,可是文件名一直显示的是随机生成的,体验不好。设置一下FileDownloadName便可。.net
核心代码以下:调试
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调用,最后效果:code
后来查了一些资料,总结了一下:
https://darchuk.net/2019/05/31/asp-net-core-web-api-returning-a-filestream/