Il problema oggi è che quando si utilizza WebApi 2 e un metodo Get async ApiController basato su, restituisce il contenuto di un file. Quando cambio il metodo Get in sincrono, funziona bene, ma non appena lo converto in async, chiude il flusso prematuramente. (Fiddler riporta la connessione è stata interrotta) Il codice sincrono di lavoro è:C# Async ApiController Chiusura OutputStream anticipatamente
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());
}
}
E la GetDownloadStreamSync è la seguente:
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);
}
}
-------- codice asincrono ----- -----
versioneAsync è esattamente lo stesso, tranne:
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();
}
Con l'implementazione asincrona di GetDo wnloadStream come segue: (streamToCopyTo è l'OutputStream dalla 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);
}
}
Stiamo cercando di abbracciare la async/await modello da davanti a dietro, quindi si spera che qualcuno è a conoscenza del motivo per cui questo sarebbe fallendo? Ho anche provato a non chiamare Response.End(), Response.Flush() e HttpContext.Current.ApplicationInstance.CompleteRequest(). Inoltre, in risposta alle domande/commenti di seguito, ho inserito un punto di interruzione sulla risposta.End() con il risultato che non è stato colpito al completamento del metodo GetDownloadStream. Forse OutputStream non è asincrono? Tutte le idee sono ben accette! Grazie
************************** Soluzione finale **************** ***********
Grande grazie a tutti coloro che hanno commentato, e in particolare a @Noseratio per il suo suggerimento su 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
}
Con il GetDownloadStreamAsync simile a questo:
FileStream fs = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.None, BufferSize, FileOptions.DeleteOnClose | FileOptions.Asynchronous);
ho lasciato che mi è stato anche la decodifica del flusso di file al volo, e questo funziona, quindi per coloro che sono interessati ...
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);
Ecco cosa la spiegazione di MSDN è: ". Un attendono espressione non blocca il filo su cui è in esecuzione, invece, fa sì che il compilatore per registrare il resto del metodo asincrono come una continuazione sull'attività attesa.Il controllo quindi ritorna al chiamante del metodo asincrono.Quando l'attività viene completata, richiama la sua continuazione, e l'esecuzione del metodo asincrono riprende da dove era stata interrotta Un'espressione di attesa può verificarsi solo nel corpo di un metodo che racchiude immediatamente, espressione lambda o metodo anonimo contrassegnato da un modificatore asincrono.Altrove, viene interpretato come un identificatore. " – user1789573
Qual è la firma del metodo in async Astuccio? (Suppongo che 'Task Get ...', ma è meglio accertarsi). –
Nel caso in cui manchi qualcosa nella tua domanda, puoi pubblicare la versione completa asincrona? – EZI