ASP.NET Core 将文件夹内容输出为压缩包文件方法
本文主要是告诉大家一个省内存的方法,将整个文件夹的内容作为一个压缩包输出,但是实际上没有申请那么多的内存,也不需要升级创建一个压缩包文件。原理是通过逐个读文件然后按照压缩包格式输出
在每个请求的方法可以拿到 HttpContext 属性,通过这个属性拿到 Response 属性,在这里可以使用 BodyWriter 属性,在这个属性里面写入的内容将会被客户端下载
而这个属性可以作为 Stream 请看下面代码
using var stream = HttpContext.Response.BodyWriter.AsStream();
在 .NET 中可以通过 ZipArchive 将一个文件夹的文件按照压缩文件格式写入,还可以设置压缩的压缩率等,可以设置文件所在文件夹的路径
通过在这个 stream 创建一个 ZipArchive 类,然后在这个类里面创建文件的方法就可以做到不断向客户端发送文件,发送的文件都在一个压缩包里面
/// <summary>
/// 将一个文件夹的内容读取为 Stream 的压缩包
/// </summary>
/// <param name="directory"></param>
/// <param name="stream"></param>
public static async Task ReadDirectoryToZipStreamAsync(DirectoryInfo directory, Stream stream)
{
var fileList = directory.GetFiles();
using var zipArchive = new ZipArchive(stream, ZipArchiveMode.Create);
foreach (var file in fileList)
{
var relativePath = file.FullName.Replace(directory.FullName, "");
if (relativePath.StartsWith("\") || relativePath.StartsWith("//"))
{
relativePath = relativePath.Substring(1);
}
var zipArchiveEntry = zipArchive.CreateEntry(relativePath, CompressionLevel.NoCompression);
using (var entryStream = zipArchiveEntry.Open())
{
using var toZipStream = file.OpenRead();
await toZipStream.CopyToAsync(stream);
}
await stream.FlushAsync();
}
}
上面的代码可以让运行的程序不需要申请和需要传输一样大的内存空间,或者不需要先执行压缩放在本地文件,可以不断读取本地文件然后上传。读取本地文件等都通过 CopyToAsync 自动设置缓存大小。如果不放心 CopyToAsync 方法设置的缓存大小,可以通过重载的方法手动设置缓存的大小
await toZipStream.CopyToAsync(stream, bufferSize: 100);
上面的代码设置了文件不要压缩,因为作为文件传输的时候,实际上我的业务是在内网传输,我的磁盘读取速度大概是 20M 一秒,而网络传输是 10M 一秒,也就是此时的压缩其实没什么意义,压缩减少的内容减少的传输时间就和压缩的时间差不多
如果小伙伴需要传输的时候压缩,请设置 zipArchive.CreateEntry 方法
当然此方法的缺点是,也许传输的时候服务器自己读取文件炸了,此时就会传输的文件不对,同时客户端不知道服务器传的对不对,因为压缩的大小没有告诉客户端。如果要告诉客户端压缩后的大小就需要先在服务器端进行压缩。本文的方法设置的是没有压缩率的压缩,大概的大小还可以告诉用户
此方法可以如何使用?在随意一个 Get 方法里面就可以通过 HttpContext 传入 Response 属性
在使用 BodyWriter 写入之前需要先设置 StatusCode 的值
HttpContext.Response.StatusCode = StatusCodes.Status200OK;
using var stream = HttpContext.Response.BodyWriter.AsStream();
假设需要返回的文件夹是 f:lindexitest
可以通过下面代码的方式将文件夹输出为压缩包
[HttpGet]
[Route("{id}")]
public async Task Get([FromRoute] string id)
{
var folder = @"f:lindexitest";
HttpContext.Response.StatusCode = StatusCodes.Status200OK;
using var stream = HttpContext.Response.BodyWriter.AsStream();
await ReadDirectoryToZipStreamAsync(new DirectoryInfo(folder), stream);
}
本地我写了一个 PowerShell 脚本运行
For ($i=0; $i -le 100000; $i++)
{
(new-object System.Net.WebClient).DownloadFile("http://localhost:5000/File/doubi", "F:lindexizip2.zip")
}
本地运行这个脚本可以看到内存其实没有 GC 也没有溢出,我运行看到内存大概在 100M 左右
获取的时候会占用一些 CPU 资源,但是很省内存
如果小伙伴有更好的方法欢迎告诉我
本文代码放在github欢迎小伙伴访问。
- 温习sql语句中JOIN的各种操作(SQL2005环境)
- 揭开ps的神秘面纱——初步认识photoshop
- 地理坐标系与投影坐标系的区别
- ExtJs学习笔记(6)_可分页的GridPanel
- PowerDesinger联系的定义及使用
- Gis链接
- TortoiseSVN文件夹及文件图标不显示解决方法 TortoiseSVN文件夹及文件图标不显示解决方法
- 地图坐标
- PowerDesigner15连接Oracle失败的解决办法
- 地图校正方法心得
- 工作流参考模型点评
- 按图索骥:SQL中数据倾斜问题的处理思路与方法
- [方法“Boolean Contains(System.Guid)”不支持转换为 SQL]的解决办法
- DataBind的一些试验
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- [译]谈谈SpringBoot 事件机制
- C++ 模板沉思录(下)
- Cocos2d-js中的简易MVC框架(四)显示层View
- 如何在去中心化交易所中(DEX)集成0x协议
- Spring Data REST不完全指南(二)
- Cocos2d-js中的简易MVC框架(三)中介者Mediator
- Cocos2d-js中的简易MVC框架(二)数据模型Model
- Python 还能实现图片去雾?FFA 去雾算法、暗通道去雾算法用起来! | 附代码
- 探秘计算机视觉中的注意力机制
- 采用 Vue 编写的功能强大的 Swagger-ui 页面
- Spring Data REST不完全指南(三)
- Spark Kafka 基于Direct自己管理offset
- 使用Reactor响应式编程
- 多场景下MySQL临时表的作用
- Flink 自定义触发器实现带超时时间的 CountWindow