简体   繁体   English

ASP.NET 核心手动处理请求体 stream

[英]ASP.NET Core manually dispose request body stream

I have a method for uploading a CSV file to server.我有一种将 CSV 文件上传到服务器的方法。 If this CSV file has 100 000 rows, then processing takes 10 minutes.如果此 CSV 文件有 100 000 行,则处理需要 10 分钟。 So I tried to return a result after 2 seconds and continue processing in background task:所以我尝试在 2 秒后返回结果并继续在后台任务中处理:

[HttpPost]
[RequestSizeLimit(53 * 1024 * 1024)]
public async Task<IActionResult> Load(IFormFile data)
{
    await await Task.WhenAny(
            Task.Delay(TimeSpan.FromSeconds(2)),
            AsyncProcessData(data));
    return RedirectToAction("Index");
}

In case of executing takes more than 2 seconds, user will see that last upload is in status "Processing", and I decided it is better, than if user has unresponding web page for several minutes.如果执行时间超过 2 秒,用户会看到最后一次上传处于“处理中”状态,我认为这比用户在几分钟内没有响应 web 页面要好。

Unfortunatly, result of this approach is that only 1760 records were processed and while reading 1761-th row an error was throwed - Cannot access a closed file.不幸的是,这种方法的结果是只处理了 1760 条记录,并且在读取第 1761 行时抛出了一个错误 - Cannot access a closed file.

As far as I can understand, after 2 seconds completed response was sended and stream with incoming data was disposed.据我了解,在 2 秒后发送完成响应并处理带有传入数据的 stream。

I would like ASP.NET infrastructure to don't dispose stream with request body so I could do it myself, something like this:我希望 ASP.NET 基础设施不要使用请求正文处理 stream,这样我就可以自己做,如下所示:

Task.WhenAny(
    Task.Delay(TimeSpan.FromSeconds(2)),
    AsyncProcessData(data).ContinueWith(_ => <dispose_stream_with_request_body>));

Can I do it?我可以做吗?

This is not possible.这是不可能的。

Think of it this way: if you return a response ( return RedirectToAction("Index"); ) then the client (the browser?) will stop sending data and therefore ending the stream even if you somehow manage to avoid disposing it server-side.这样想:如果您返回响应( return RedirectToAction("Index"); ),那么客户端(浏览器?)将停止发送数据并因此结束 stream 即使您设法避免在服务器端处理它. If the client stops sending data there's nothing you can do server-side.如果客户端停止发送数据,则您无法在服务器端执行任何操作。

A couple of suggestions for what you can do...关于你可以做什么的一些建议......

Load all data into memory将所有数据加载到 memory

This is probably the closest you can get to your current workflow.这可能是最接近当前工作流程的方法。

I'm not sure what happens in AsyncProcessData but I imagine you are line-by-line/chunk-by-chunk reading data from the request stream and doing some processing on it one at a time.我不确定AsyncProcessData中会发生什么,但我想您正在逐行/逐块地从请求 stream 中读取数据,并一次对其进行一些处理。 If you instead write the whole stream into another stream or similar and then use that new stream instead for your processing, you can schedule the processing on a background thread and return a response as soon as the stream is copied. If you instead write the whole stream into another stream or similar and then use that new stream instead for your processing, you can schedule the processing on a background thread and return a response as soon as the stream is copied.

Note : this means you are keeping the whole payload from the request in-memory, which does not scale very well.注意:这意味着您将请求的整个有效负载保留在内存中,这不能很好地扩展。 In other words, if you allow uploading large files or many users are using it at the same time, you'll likely run out of memory quickly.换句话说,如果您允许上传大文件或许多用户同时使用它,您可能会很快用完 memory。

Saving to disk/DB and processing asynchronously保存到磁盘/数据库并异步处理

I would suggest something like the following instead.我会建议类似以下内容。

When Load is called you save the payload (the CSV-file) to a temporary file on your disk or in a database.当调用Load时,您将有效负载(CSV 文件)保存到磁盘或数据库中的临时文件中。 Then you schedule the processing of this file on a background thread and return a response like you do now.然后您安排在后台线程上处理此文件并像现在一样返回响应。 Here you probably need to delete the temporary file (from disk or in the database) when you're done.完成后,您可能需要在此处删除临时文件(从磁盘或数据库中)。

Note : like the memory issue with the other solution here you need to ensure you have the necessary disk space.注意:像 memory 问题与此处的其他解决方案一样,您需要确保有必要的磁盘空间。 Disk space is usually a lot cheaper than memory, so this is much more scalable.磁盘空间通常比 memory 便宜很多,因此它更具可扩展性。 You do, however, need to ensure you remember to delete the file when you're done (also if something goes wrong and your app crashes).但是,您确实需要确保在完成后记得删除文件(如果出现问题并且您的应用程序崩溃)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM