今天的问题是,在使用WebApi 2和异步ApiController的Get方法时,返回文件内容。当我将Get方法更改为同步时,它可以正常工作,但是一旦我将其转换回异步,它会过早关闭流(Fiddler报告连接中止)。可工作的同步代码如下:
通过以下异步实现的GetDownloadStream:(streamToCopyTo是来自response.OutputStream的OutputStream)
我们正在尝试从前端到后端采用异步/等待模式,希望有人知道为什么会失败?我也尝试过不调用Response.End(),Response.Flush()和HttpContext.Current.ApplicationInstance.CompleteRequest()。另外,回应下面的问题/评论,我在response.End()上放了一个断点,结果它没有被触发,以致于GetDownloadStream方法已经完成。也许OutputStream不是异步的?欢迎任何想法!谢谢。
************************** 最终解决方案 ***************************
非常感谢所有发表评论的人,特别是@Noseratio对FileOptions.DeleteOnClose的建议。
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);
Task Get...
,但最好确定一下)。 - Alexei Levenkov