异步处理传入的 FileStream
Process incoming FileStream asynchronously
我正在读取用户上传的文件,它正在同步工作。我需要更改它以便立即向用户发送“已收到”警报,然后异步读取文件,同时用户会定期轮询以查看读取是否完成。
这是我的代码现在的样子:
public FileUpload SaveFile(Stream stream)
{
FileUpload uploadObj = //instantiate the return obj
var task = Task.Run(async () => await ProcessFileAsync(stream));
return upload;
}
public async Task ProcessFileAsync(Stream stream)
{
StreamReader file = new StreamReader(stream);
CsvReader csv = new CsvReader(file, CultureInfo.InvariantCulture);
while (await csv.ReadAsync())
{
//read the file
}
}
我遇到的问题是,当我调用 csv.ReadAsync()
方法时,Stream 对象已被释放。当我希望 SaveFile()
方法向用户 return 一个值时如何访问 Stream,但是 returning 的行为处理了 Stream 对象?
如果环境允许,您需要执行此操作:
var result = task.Result;
//do stuff
...或
public Task<FileUpload> SaveFile(Stream stream)
{
var uploadObj = //instantiate the return obj
await ProcessFileAsync(stream);
return uploadObj;
}
如果您走那条路,请参阅此处以获得关于 fire-and-forget 的详尽讨论:
Web Api - Fire and Forget
这里的要点是您在 ASP.NET 的约束下工作,它抽象出了很多底层 HTTP 内容。
当您说要异步处理 user-uploaded 文件时,您想要跳出使用 HTTP 和 ASP.NET 处理事情的正常顺序。您会看到,当客户端发送带有 body(文件)的请求时,服务器会收到请求 headers 并启动 ASP.NET 以告诉您的应用程序代码有一个新的请求传入.
此时它甚至还没有(完全)读取请求 body。这就是为什么你得到一个 Stream 来处理请求,而不是一个字符串或文件名——数据还没有到达服务器!只是请求 headers,将请求通知 Web 服务器。
如果您return a response at that point,就所有 HTTP 和 ASP.NET 而言,您已完成请求,无法继续阅读其 body。
现在您要做的是读取请求body(文件),并在向客户端发送响应后处理。你可以这样做,但是你仍然需要阅读请求 body - 因为如果你在阅读请求之前 return 来自你的操作方法的东西,框架会认为你已经完成了它并处理请求流。这就是导致您出现异常的原因。
如果您使用字符串、模型绑定或任何涉及框架读取请求的内容 body,那么是的,您的代码只会在 body 被读取后执行.
short-term 解决方案似乎可以让您继续前进,是 read the request stream into a stream that you own,而不是框架:
var myStream = new MemoryStream();
await stream.CopyTo(myStream);
Task.Run(async () => await ProcessFileAsync(myStream));
现在您已经读取了整个请求 body 并将其保存在内存中,因此 ASP.NET 可以安全地处理请求流并向客户端发送响应。
但是不要这样做。从控制器启动 fire-and-forget 任务是个坏主意。将上传的文件保存在内存中是个坏主意。
你实际上应该做什么,如果你还想这样做的话out-of-band:
- 将传入文件另存为服务器上的实际临时文件
- 使用标识符(临时生成的文件名,例如 GUID)向客户端发送响应
- 公开一个端点,客户端可以使用所述 GUID 来请求状态
- 让后台进程不断扫描目录以查找新上传的文件并进行处理
对于后者,您可以 hosted services 或 third-party 像 Hangfire 这样的工具。
我正在读取用户上传的文件,它正在同步工作。我需要更改它以便立即向用户发送“已收到”警报,然后异步读取文件,同时用户会定期轮询以查看读取是否完成。
这是我的代码现在的样子:
public FileUpload SaveFile(Stream stream)
{
FileUpload uploadObj = //instantiate the return obj
var task = Task.Run(async () => await ProcessFileAsync(stream));
return upload;
}
public async Task ProcessFileAsync(Stream stream)
{
StreamReader file = new StreamReader(stream);
CsvReader csv = new CsvReader(file, CultureInfo.InvariantCulture);
while (await csv.ReadAsync())
{
//read the file
}
}
我遇到的问题是,当我调用 csv.ReadAsync()
方法时,Stream 对象已被释放。当我希望 SaveFile()
方法向用户 return 一个值时如何访问 Stream,但是 returning 的行为处理了 Stream 对象?
如果环境允许,您需要执行此操作:
var result = task.Result;
//do stuff
...或
public Task<FileUpload> SaveFile(Stream stream)
{
var uploadObj = //instantiate the return obj
await ProcessFileAsync(stream);
return uploadObj;
}
如果您走那条路,请参阅此处以获得关于 fire-and-forget 的详尽讨论: Web Api - Fire and Forget
这里的要点是您在 ASP.NET 的约束下工作,它抽象出了很多底层 HTTP 内容。
当您说要异步处理 user-uploaded 文件时,您想要跳出使用 HTTP 和 ASP.NET 处理事情的正常顺序。您会看到,当客户端发送带有 body(文件)的请求时,服务器会收到请求 headers 并启动 ASP.NET 以告诉您的应用程序代码有一个新的请求传入.
此时它甚至还没有(完全)读取请求 body。这就是为什么你得到一个 Stream 来处理请求,而不是一个字符串或文件名——数据还没有到达服务器!只是请求 headers,将请求通知 Web 服务器。
如果您return a response at that point,就所有 HTTP 和 ASP.NET 而言,您已完成请求,无法继续阅读其 body。
现在您要做的是读取请求body(文件),并在向客户端发送响应后处理。你可以这样做,但是你仍然需要阅读请求 body - 因为如果你在阅读请求之前 return 来自你的操作方法的东西,框架会认为你已经完成了它并处理请求流。这就是导致您出现异常的原因。
如果您使用字符串、模型绑定或任何涉及框架读取请求的内容 body,那么是的,您的代码只会在 body 被读取后执行.
short-term 解决方案似乎可以让您继续前进,是 read the request stream into a stream that you own,而不是框架:
var myStream = new MemoryStream();
await stream.CopyTo(myStream);
Task.Run(async () => await ProcessFileAsync(myStream));
现在您已经读取了整个请求 body 并将其保存在内存中,因此 ASP.NET 可以安全地处理请求流并向客户端发送响应。
但是不要这样做。从控制器启动 fire-and-forget 任务是个坏主意。将上传的文件保存在内存中是个坏主意。
你实际上应该做什么,如果你还想这样做的话out-of-band:
- 将传入文件另存为服务器上的实际临时文件
- 使用标识符(临时生成的文件名,例如 GUID)向客户端发送响应
- 公开一个端点,客户端可以使用所述 GUID 来请求状态
- 让后台进程不断扫描目录以查找新上传的文件并进行处理
对于后者,您可以 hosted services 或 third-party 像 Hangfire 这样的工具。