当前位置: 首页 > news >正文

filestream读取文件_ASP.NET Core WebAPI文件下载

最近要使用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 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 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调用,最后效果:

a39d235113acfafefb52c8cc60f51480.png

总结

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

  • 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/

除非特殊说明,本作品由podolski创作,采用知识共享署名 4.0 国际许可协议进行许可。欢迎转载,转载请保留原文链接~喜欢的观众老爷们可以点下关注或者推荐~

出处:

https://www.cnblogs.com/podolski/archive/2020/04/11/12682978.html

版权申明:本文来源于网友收集或网友提供,如果有侵权,请转告版主或者留言,本公众号立即删除。

3ea48adef9f031b9eab1b52c6aea3ca6.gif

相关文章:

  • python怎么循环合并数组_python数组循环合并python执行系统命令四种方法比较
  • git pull 强制覆盖本地_用git简单粗暴地完成本地、服务器同步
  • github可视化工具_深度学习训练过程可视化(附github源码)
  • grep 与条件_【125】Linux 中 ps ef|grep和ps、grep详解
  • linux搜索文件_学习+使用Linux的最佳姿势,收录近600条Linux系统命令
  • onblur事件怎么触发_JavaScript第十三章节 事件
  • html一个页面中切换多个页面_前端入门教程---从0开始通过一个商城实例手把手教你学习PC端和移动端页面开发第2章HTML基础知识...
  • window.location.href 设置请求头_常见的http响应的返回头
  • java逆向工程_图书推荐安卓高级逆向工程师技能树
  • rangechecks 检测代码检测到超出范围的数组访问。_夯实基础系列(一)数据类型及其检测及进阶...
  • python示例程序演示_以Python代码实例展示kNN算法的实际运用
  • python 自动下载脚本_Python脚本自动下载小说
  • jdbc封装工具类代码_[22]-JDBC 工具类优化
  • jpg转dwg格式转换器_如何将PDF或者JPG转CAD格式(dwg格式)?
  • python多线程实现生产者消费者_使用Python多线程实现生产者与消费者模型
  • CSS中外联样式表代表的含义
  • exif信息对照
  • GDB 调试 Mysql 实战(三)优先队列排序算法中的行记录长度统计是怎么来的(上)...
  • JAVA 学习IO流
  • Java多线程(4):使用线程池执行定时任务
  • Mithril.js 入门介绍
  • Mysql优化
  • React中的“虫洞”——Context
  • socket.io+express实现聊天室的思考(三)
  • Stream流与Lambda表达式(三) 静态工厂类Collectors
  • ViewService——一种保证客户端与服务端同步的方法
  • Vue--数据传输
  • vue--为什么data属性必须是一个函数
  • WePY 在小程序性能调优上做出的探究
  • 阿里中间件开源组件:Sentinel 0.2.0正式发布
  • 如何进阶一名有竞争力的程序员?
  • 微信公众号开发小记——5.python微信红包
  • 异步
  • 这几个编码小技巧将令你 PHP 代码更加简洁
  • 白色的风信子
  • 如何用纯 CSS 创作一个菱形 loader 动画
  • ​​​​​​​ubuntu16.04 fastreid训练过程
  • ​卜东波研究员:高观点下的少儿计算思维
  • ###项目技术发展史
  • #前后端分离# 头条发布系统
  • (2.2w字)前端单元测试之Jest详解篇
  • (AngularJS)Angular 控制器之间通信初探
  • (C语言)求出1,2,5三个数不同个数组合为100的组合个数
  • (HAL)STM32F103C6T8——软件模拟I2C驱动0.96寸OLED屏幕
  • (Matalb时序预测)WOA-BP鲸鱼算法优化BP神经网络的多维时序回归预测
  • (Redis使用系列) Springboot 使用redis的List数据结构实现简单的排队功能场景 九
  • (附源码)springboot青少年公共卫生教育平台 毕业设计 643214
  • (生成器)yield与(迭代器)generator
  • (十六)Flask之蓝图
  • (提供数据集下载)基于大语言模型LangChain与ChatGLM3-6B本地知识库调优:数据集优化、参数调整、Prompt提示词优化实战
  • (图)IntelliTrace Tools 跟踪云端程序
  • (心得)获取一个数二进制序列中所有的偶数位和奇数位, 分别输出二进制序列。
  • (转)大型网站的系统架构
  • **PHP分步表单提交思路(分页表单提交)
  • .babyk勒索病毒解析:恶意更新如何威胁您的数据安全