最近要使用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文件下载的更多相关文章

  1. asp.net core webapi之跨域(Cors)访问

    这里说的跨域是指通过js在不同的域之间进行数据传输或通信,比如用ajax向一个不同的域请求数据,或者通过js获取页面中不同域的框架中(iframe)的数据.只要协议.域名.端口有任何一个不同,都被当作 ...

  2. ASP.NET Core WebAPI 开发-新建WebAPI项目

    ASP.NET Core WebAPI 开发-新建WebAPI项目, ASP.NET Core 1.0 RC2 即将发布,我们现在来学习一下 ASP.NET Core WebAPI开发. 网上已经有泄 ...

  3. Asp.net Core WebApi 使用Swagger做帮助文档,并且自定义Swagger的UI

    WebApi写好之后,在线帮助文档以及能够在线调试的工具是专业化的表现,而Swagger毫无疑问是做Docs的最佳工具,自动生成每个Controller的接口说明,自动将参数解析成json,并且能够在 ...

  4. Asp.Net Core WebApi学习笔记(四)-- Middleware

    Asp.Net Core WebApi学习笔记(四)-- Middleware 本文记录了Asp.Net管道模型和Asp.Net Core的Middleware模型的对比,并在上一篇的基础上增加Mid ...

  5. Asp.net core WebApi 使用Swagger生成帮助页

    最近我们团队一直进行.net core的转型,web开发向着前后端分离的技术架构演进,我们后台主要是采用了asp.net core webapi来进行开发,开始每次调试以及与前端人员的沟通上都存在这效 ...

  6. ASP.Net Core WebApi几种版本控制对比

    版本控制的好处: (1)助于及时推出功能, 而不会破坏现有系统. (2)它还可以帮助为选定的客户提供额外的功能. API 版本控制可以采用不同的方式进行控制,方法如下: (1)在 URL 中追加版本或 ...

  7. ASP.NET Core WebApi 返回统一格式参数(Json 中 Null 替换为空字符串)

    相关博文:ASP.NET Core WebApi 返回统一格式参数 业务场景: 统一返回格式参数中,如果包含 Null 值,调用方会不太好处理,需要替换为空字符串,示例: { "respon ...

  8. ASP.NET Core WebApi使用Swagger生成api说明文档看这篇就够了

    引言 在使用asp.net core 进行api开发完成后,书写api说明文档对于程序员来说想必是件很痛苦的事情吧,但文档又必须写,而且文档的格式如果没有具体要求的话,最终完成的文档则完全取决于开发者 ...

  9. Asp.Net Core WebApi中接入Swagger组件(初级)

    开发WebApi时通常需要为调用我们Api的客户端提供说明文档.Swagger便是为此而存在的,能够提供在线调用.调试的功能和API文档界面. 环境介绍:Asp.Net Core WebApi + S ...

随机推荐

  1. C语言程序设计(十一) 指针和数组

    第十一章 指针和数组 一旦给出数组的定义,编译系统就会为其在内存中分配固定的存储单元,相应的,数组的首地址也就确定了 C语言中的数组名有特殊的含义,它代表存放数组元素的连续存储空间的首地址 //L11 ...

  2. MySQL记录操作(增删改)

    概览 MySQL数据操作: DML 在MySQL管理软件中,可以通过SQL语句中的DML语言来实现数据的操作,包括 使用INSERT实现数据的插入 UPDATE实现数据的更新 使用DELETE实现数据 ...

  3. 吐血干货,直播首屏耗时400ms以下的优化实践

    导读: 直播行业的竞争越来越激烈,进过18年这波洗牌后,已经度过了蛮荒暴力期,剩下的都是在不断追求体验.最近在帮做直播优化首开,通过多种方案并行,把首开降到500ms以下,希望能对大家有借鉴. 背景: ...

  4. vue基础响应式数据

    1.vue 采用 v……vm……m,模式,v---->el,vm---->new Vue(实例),m---->data 数据,让前端从操作大量的dom元素中解放出来. 2.vue响应 ...

  5. 从 Spring 的环境到 Spring Cloud 的配置

    需求 不知不觉,web 开发已经进入 “微服务”.”分布式” 的时代,致力于提供通用 Java 开发解决方案的 Spring 自然不甘人后,提出了 Spring Cloud 来扩大 Spring 在微 ...

  6. 2. python提示:TypeError: unhashable type: 'list'

    原因是,python字典的key不支持list类型和dict类型,需要转换 错误时 将list类型强制转换成string,用"".join(list). 修改后:

  7. python爬取某站新闻,并分析最近新闻关键词

    在爬取某站时并做简单分析时,遇到如下问题和大家分享,避免犯错: 一丶网站的path为 /info/1013/13930.htm ,其中13930为不同新闻的 ID 值,但是这个数虽然为升序,但是没有任 ...

  8. SqlBulkCopy批量插入数据 显示 来自数据源的 String 类型的给定值不能转换为指定目标列的类型 smalldatetime。错误

    因为需要大量插入数据,linq ef无法达到速度的要求,因此把模型转换成SQL ,使用SqlBulkCopy快速插入.但是去提示 来自数据源的 String 类型的给定值不能转换为指定目标列的类型 s ...

  9. Linux开机启动程序rc.local

    目录 1./etc/rc.local是/etc/rc.d/rc.local的软链接 2.rc.local文件的原始内容 3.rc.local文件的配置 4.应用经验 5.版权声明 在CentOS7中, ...

  10. Python-标准模块与第三方模块

    标准模块 python官方自己的模块 os os与os.path os.chdir('desktop')        跳转至指定目录 os.listdir()                    ...