Das Problem bei der Verwendung von WebApi 2 und einer auf Async ApiController basierenden Get-Methode besteht darin, dass der Inhalt einer Datei zurückgegeben wird. Wenn ich die Get-Methode auf "synchron" ändere, funktioniert das problemlos, aber sobald ich es wieder in async konvertiere, schließt es den Stream vorzeitig. (Fiedler, berichtet die Verbindung abgebrochen wurde) Der Arbeits Synchron-Code ist:C# Async ApiController Vorzeitiges Schließen von OutputStream
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());
}
}
Und das GetDownloadStreamSync ist wie folgt:
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);
}
}
-------- Async-Code ----- -----
die Async-Version ist genau das gleiche, außer:
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();
}
Mit der Asynchron-Implementierung von GetDo wnloadStream wie folgt: (streamToCopyTo ist die Output vom Response.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);
}
}
Wir versuchen, die async/await Muster von vorne zu umarmen, zu sichern, so hoffentlich jemand ist sich bewusst, warum dies nicht möglich wäre? Ich habe auch versucht, Response.End(), Response.Flush() und HttpContext.Current.ApplicationInstance.CompleteRequest() nicht aufzurufen. Als Reaktion auf die folgenden Fragen/Kommentare habe ich auch einen Haltepunkt auf die Antwort platziert.End() mit dem Ergebnis, dass die GetDownloadStream-Methode nicht getroffen wurde. Vielleicht ist der OutputStream nicht asynchron? Irgendwelche Ideen sind willkommen! Dank
************************** Endlösung **************** ***********
Vielen Dank an alle, die kommentiert haben, und besonders an @Noseratio für seinen Vorschlag zu 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
}
Mit dem GetDownloadStreamAsync wie folgt aussehen:
FileStream fs = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.None, BufferSize, FileOptions.DeleteOnClose | FileOptions.Asynchronous);
verließ ich heraus, dass ich auch im Fluge den Dateistrom wurde zu entschlüsseln, und dies funktioniert, so für die Interessenten ...
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);
Hier ist, was MSDN Erklärung ist. „Ein Ausdruck erwarten den Faden nicht blockieren, auf dem es ausgeführt wird Stattdessen bewirkt, dass es die Compiler, um den Rest der asynchronen Methode als Fortsetzung der erwarteten Aufgabe zu registrieren Control kehrt dann zum Aufrufer der asynchronen Methode zurück Wenn die Task abgeschlossen ist, ruft sie ihre Fortsetzung auf und die Ausführung der asynchronen Methode wird dort fortgesetzt, wo sie abgebrochen wurde Ein await-Ausdruck kann nur im Rumpf einer unmittelbar umschließenden Methode, eines Lambda-Ausdrucks oder einer anonymen Methode auftreten, die durch einen asynchronen Modifizierer markiert ist. Andernfalls wird sie als Bezeichner interpretiert. " – user1789573
Was ist die Signatur der Methode in async? Fall? (Ich nehme 'Task Get ...' an, aber es ist besser sicher). –
Falls Sie etwas in Ihrer Frage vermissen, können Sie die vollständige Async-Version veröffentlichen? – EZI