如何解决附加到 C# .NET Core 中的响应正文
我正在开发一个项目,在该项目中我在后端填充一些 pdf,然后我将这些 pdf 转换为 byte[] 列表,该列表合并到一个非常大的数组中,最后通过响应正文发回作为内存流。 我的问题是这是大量数据,在获取要合并的字节数组列表的过程中,我使用了大量内存。 我想知道是否不是将最终合并的字节 [] 转换为内存流并将其添加到响应正文中;我可以创建几个 Memory Stream 对象,并在创建它们时附加到 Response.Body 吗?或者,我想知道是否有一种方法可以使用一个 Memory Stream 并继续添加它作为为每个 pdf 文档创建每个新字节 []?
编辑:这可能有点啰嗦,但我的原始帖子太含糊了。在我尝试做的核心工作中,我有几个 pdf 文档,每个文档都有几页长。它们中的每一个都在下面的代码中表示为 filesToMerge 列表中的 byte[] 项目之一。理想情况下,我想一一处理这些并将它们转换为内存流,然后在循环中将它们一个接一个地发送给客户端。但是,当我尝试这样做时,我收到响应正文已发送的错误。有没有办法将某些内容附加到响应正文中,以便每次通过循环进行更新?
[HttpGet("template/{formId}/fillforms")]
public void FillForms(string formId/*,[FromBody] IList<IDictionary<string,string>> fieldDictionaries*/)
{
List<byte[]> filesToMerge = new List<byte[]>();
// For testing
var mockData = new MockData();
IList<IDictionary<string,string>> fieldDictionaries = mockData.GetMock1095Dictionaries();
foreach(IDictionary<string,string> dictionary in fieldDictionaries)
{
var populatedForm = this.dataRepo.PopulateForm(formId,dictionary);
// write to rb
filesToMerge.Add(populatedForm);
}
byte[] mergedFilesAsByteArray = this.dataRepo.GetMergedByteArray(filesToMerge);
this.SendResponse(formId + "_filled.pdf",new MemoryStream(mergedFilesAsByteArray));
}
private void SendResponse(string formName,MemoryStream ms,IDictionary<string,string> fieldData = null)
{
Response.Clear();
Response.ContentType = "application/pdf";
Response.Headers.Add("content-disposition",$"attachment;filename={formName}.pdf");
ms.Writeto(Response.Body);
}
解决方法
内存流实际上只是字节数组,上面有一堆不错的方法。所以切换到字节数组不会有太大帮助。人们在处理字节数组和内存流时遇到的一个问题是,当您处理完数据时不会释放内存,因为它们占用了您正在运行的机器的内存,因此您很容易耗尽内存。因此,您应该在不再需要数据时立即以“using statements”为例进行处理。内存流有一个叫做 Dispose 的方法,它会释放流使用的所有资源
如果您想尽快从应用程序传输数据,最好的方法是将流切割成更小的部分,然后在目的地以正确的顺序重新组合它们。你可以把它们切成 1mb 或 126kb,不管你想要什么。当你将数据发送到目的地时,你还需要传递这部分的订单号是多少,因为这种方法允许你并行POST数据,并且没有顺序保证。
将一个流拆分为多个流
private static List<MemoryStream> CreateChunks(Stream stream)
{
byte[] buffer = new byte[4000000]; //set the size of your buffer (chunk)
var returnStreams = new List<MemoryStream>();
using (MemoryStream ms = new MemoryStream())
{
while (true) //loop to the end of the file
{
var returnStream = new MemoryStream();
int read = stream.Read(buffer,buffer.Length); //read each chunk
returnStream.Write(buffer,read); //write chunk to [wherever];
if (read <= 0)
{ //check for end of file
return returnStreams;
}
else
{
returnStream.Position = 0;
returnStreams.Add(returnStream);
}
}
}
}
然后我遍历创建的流以创建要发布到服务的任务,并且每个任务都将发布到服务器。我会等待所有任务完成,然后再次调用我的服务器,告诉它我已经完成上传,它可以按照正确的顺序将所有数据合并为一个。我的服务有一个上传会话的概念来跟踪所有的部分以及它们进入的顺序。它还会在每个部分进入时将它们保存到数据库中;就我而言,Azure Blob 存储。
,不清楚为什么将多个 MemoryStream
的内容复制到 Response.Body
时会出错。您当然应该能够做到这一点,尽管您需要确保在开始写入数据后不要尝试更改响应标头或状态代码(也不要在开始写入后尝试调用 Response.Clear()
数据)。
这是一个开始响应然后写入数据的简单示例:
[ApiController]
[Route("[controller]")]
public class RandomDataController : ControllerBase {
private readonly ILogger<RandomDataController> logger;
private const String CharacterData = "abcdefghijklmnopqrstuvwxyz0123456789 ";
public RandomDataController(ILogger<RandomDataController> logger) {
this.logger = logger;
}
[HttpGet]
public async Task Get(CancellationToken cancellationToken) {
this.Response.ContentType = "text/plain";
this.Response.ContentLength = 1000;
await this.Response.StartAsync(cancellationToken);
logger.LogInformation("Response Started");
var rand = new Random();
for (var i = 0; i < 1000; i++) {
// You should be able to copy the contents of a MemoryStream or other buffer here instead of sending random data like this does.
await this.Response.Body.WriteAsync(Encoding.UTF8.GetBytes(CharacterData[rand.Next(0,CharacterData.Length)].ToString()),cancellationToken);
Thread.Sleep(50); // This is just to demonstrate that data is being sent to the client as it is written
cancellationToken.ThrowIfCancellationRequested();
if (i % 100 == 0 && i > 0) {
logger.LogInformation("Response In Flight {PercentComplete}",(Double)i / 1000);
}
}
logger.LogInformation("Response Complete");
}
}
您可以使用 netcat 验证此数据流回客户端:
% nc -nc 127.0.0.1 5000
GET /randomdata HTTP/1.1
Host: localhost:5000
Connection: Close
(在 Connection: Close
后输入一个额外的空行以开始请求)。当数据写入服务器上的 Response.Body
时,您应该会看到数据出现在 netcat 中。
需要注意的一点是,这种方法涉及预先计算要发送的数据的长度。如果您无法预先计算响应的大小,或者不想计算,您可以查看 Chunked Transfer Encoding,如果您开始将数据写入 Response.Body 而不指定 similar issue,ASP.Net 应该自动使用它{1}}。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。