gpt4 book ai didi

C# Async ApiController 过早关闭 OutputStream

转载 作者:太空狗 更新时间:2023-10-29 19:45:18 26 4
gpt4 key购买 nike

现在的问题是,当使用 WebApi 2 和基于 Async ApiController 的 Get 方法时,它会返回文件的内容。当我将 Get 方法更改为同步时,它工作得很好,但一旦我将其转换回异步,它就会过早地关闭流。 (Fiddler 报告连接已中止)工作的同步代码是:

 public void Get(int id)
{
try
{
FileInfo fileInfo = logic.GetFileInfoSync(id);
HttpResponse response = HttpContext.Current.Response;
response.Clear();
response.ClearContent();
response.Buffer = true;
response.AddHeader("Content-Disposition", "attachment; filename=\"" + fileInfo.Node.Name + fileInfo.Ext + "\"");
response.AddHeader("Content-Length", fileInfo.SizeInBytes.ToString());
response.ContentType = "application/octet-stream";
logic.GetDownloadStreamSync(id, response.OutputStream);
response.StatusCode = (int)HttpStatusCode.OK;
//HttpContext.Current.ApplicationInstance.CompleteRequest();
response.End();
}
catch(Exception ex)
{
Console.WriteLine(ex.ToString());
}
}

GetDownloadStreamSync 如下:

public async Task GetDownloadStream(string fileIdentifier, Stream streamToCopyTo)
{
string filePath = Path.Combine(fileIdentifierFolder, fileIdentifier);
using (FileStream fs = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.None, BufferSize, false))
{
fs.CopyTo(streamToCopyTo);
}
}

--------异步代码------------

异步版本完全相同,除了:

public async Task Get(int id)
{
FileInfo fileInfo = await logic.GetFileInfoSync(id); // database opp
HttpResponse response = HttpContext.Current.Response;
response.Clear();
response.ClearContent();
response.Buffer = true;
response.AddHeader("Content-Disposition", "attachment; filename=\"" + fileInfo.Node.Name + fileInfo.Ext + "\"");
response.AddHeader("Content-Length", fileInfo.SizeInBytes.ToString());
response.ContentType = "application/octet-stream";
await logic.GetDownloadStreamSync(id, response.OutputStream);
//database opp + file I/O
response.StatusCode = (int)HttpStatusCode.OK;
//HttpContext.Current.ApplicationInstance.CompleteRequest();
response.End();
}

使用 GetDownloadStream 的异步实现如下:(streamToCopyTo 是来自 response.OutputStream 的 OutputStream)

    public async Task GetDownloadStream(string fileIdentifier, Stream streamToCopyTo)
{
using (FileStream fs = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.None, BufferSize, true))
{
await fs.CopyToAsync(streamToCopyTo);
}
}

我们正在尝试从头到尾采用异步/等待模式,所以希望有人知道为什么会失败?我也试过不调用 Response.End()、Response.Flush() 和 HttpContext.Current.ApplicationInstance.CompleteRequest()。此外,为了回应下面的问题/评论,我在 response.End() 上放置了一个断点,结果是它没有被命中到 GetDownloadStream 方法已经完成。也许 OutputStream 不是异步的?欢迎任何想法!谢谢

************************** 最终解决方案 ******************* ********

非常感谢所有发表评论的人,尤其是@Noseratio 对 FileOptions.DeleteOnClose 的建议。

[HttpGet]
public async Task<HttpResponseMessage> Get(long id)
{
HttpResponseMessage result = new HttpResponseMessage(HttpStatusCode.OK);
Node node = await logic.GetFileInfoForNodeAsync(id);

result.Content = new StreamContent(await logic.GetDownloadStreamAsync(id));
result.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
result.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment")
{
FileName = node.Name + node.FileInfo.Extension
};
result.Content.Headers.ContentLength = node.FileInfo.SizeInBytes;
return result
}

GetDownloadStreamAsync 看起来像这样:

 FileStream fs = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.None, BufferSize, FileOptions.DeleteOnClose | FileOptions.Asynchronous);

我忽略了我也在动态解密文件流,这确实有效,所以对于那些感兴趣的人......

FileStream fs = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.None, BufferSize, FileOptions.DeleteOnClose | FileOptions.Asynchronous);
RijndaelManaged rm = new RijndaelManaged();
return new CryptoStream(fs, GetDecryptor(rm, password), CryptoStreamMode.Read);

最佳答案

需要一个完整的重现案例才能回答您的确切问题,但我认为您根本不需要 async/await 。我还认为您应该尽可能避免直接使用 HttpContext.Current.Response,尤其是在异步 WebAPI Controller 方法中。

在这种特殊情况下,您可以使用 HttpResponseMessage:

[HttpGet]
public HttpResponseMessage Get(int id)
{
HttpResponseMessage result = new HttpResponseMessage(HttpStatusCode.OK);
FileInfo fileInfo = logic.GetFileInfoSync(id);

FileStream fs = new FileStream(
filePath, FileMode.Open, FileAccess.Read, FileShare.None, BufferSize, false);

result.Content = new StreamContent(fs);
result.Content.Headers.ContentType =
new MediaTypeHeaderValue("application/octet-stream");
result.Content.Headers.ContentDisposition =
new ContentDispositionHeaderValue("attachment")
{
FileName = fileInfo.Node.Name + fileInfo.Ext
};
result.Content.Headers.ContentLength = fileInfo.SizeInBytes;

return result;
}

这里没有明确的异步,所以方法不是async。但是,如果您仍然需要引入一些 await,方法将如下所示:

[HttpGet]
public async Task<HttpResponseMessage> Get(int id)
{
HttpResponseMessage result = new HttpResponseMessage(HttpStatusCode.OK);
// ...
await fs.CopyToAsync(streamToCopyTo)
// ...
return result;
}

关于C# Async ApiController 过早关闭 OutputStream,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28439254/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com