Request.Content.ReadAsMultipartAsync non restituisce mai

Ho un’API per un sistema scritto utilizzando ASP.NET Web Api e sto cercando di estenderlo per consentire il caricamento delle immagini. Ho fatto qualche ricerca su google e ho trovato il modo consigliato di accettare i file usando MultpartMemoryStreamProvider e alcuni metodi asincroni, ma la mia attesa su ReadAsMultipartAsync non ritorna mai.

Ecco il codice:

[HttpPost] public async Task LowResImage(int id) { if (!Request.Content.IsMimeMultipartContent()) { throw new HttpResponseException(HttpStatusCode.UnsupportedMediaType); } var provider = new MultipartMemoryStreamProvider(); try { await Request.Content.ReadAsMultipartAsync(provider); foreach (var item in provider.Contents) { if (item.Headers.ContentDisposition.FileName != null) { } } return Request.CreateResponse(HttpStatusCode.OK); } catch (System.Exception e) { return Request.CreateErrorResponse(HttpStatusCode.InternalServerError, e); } } 

Posso passare fino a:

 await Request.Content.ReadAsMultipartAsync(provider); 

a quel punto non si completerà mai.

Qual è la ragione per cui la mia attesa non ritorna mai?

    Aggiornare

    Sto tentando di postare questa azione usando curl, il comando è il seguente:

     C:\cURL>curl -i -F [email protected]:\LowResExample.jpg http://localhost:8000/Api/Photos/89/LowResImage 

    Ho anche provato a usare il seguente html su POST per l’azione e succede la stessa cosa:

     

    Mi sono imbattuto in qualcosa di simile in .NET 4.0 (senza async / attendi). Usando lo stack Thread del debugger, potrei dire che ReadAsMultipartAsync stava lanciando l’attività sullo stesso thread, quindi sarebbe deadlock. Ho fatto qualcosa del genere:

     IEnumerable parts = null; Task.Factory .StartNew(() => parts = Request.Content.ReadAsMultipartAsync().Result.Contents, CancellationToken.None, TaskCreationOptions.LongRunning, // guarantees separate thread TaskScheduler.Default) .Wait(); 

    Il parametro TaskCreationOptions.LongRunning era la chiave per me perché senza di esso, la chiamata continuava a lanciare l’attività sullo stesso thread. Potresti provare a utilizzare qualcosa come il seguente pseudocodice per vedere se funziona in C # 5.0:

     await TaskEx.Run(async() => await Request.Content.ReadAsMultipartAsync(provider)) 

    Ho incontrato lo stesso problema con tutto il moderno framework 4.5.2.

    Il mio metodo API accetta uno o più file caricati tramite la richiesta POST con contenuto multipart. Ha funzionato bene con piccoli file, ma con quelli grandi, il mio metodo è stato impiccato per sempre perché la funzione ReadAsMultipartAsync() non è mai stata completata.

    Cosa mi ha aiutato: utilizzare un metodo di controllo async e await che ReadAsMultipartAsync() sia completato, invece di ottenere il risultato dell’attività in un metodo di controllo sincrono.

    Quindi, questo non ha funzionato:

     [HttpPost] public IHttpActionResult PostFiles() { return Ok ( Request.Content.ReadAsMultipartAsync().Result .Contents .Select(content => ProcessSingleContent(content)) ); } private string ProcessSingleContent(HttpContent content) { return SomeLogic(content.ReadAsByteArrayAsync().Result); } 

    E questo ha funzionato:

     [HttpPost] public async Task PostFiles() { return Ok ( await Task.WhenAll ( (await Request.Content.ReadAsMultipartAsync()) .Contents .Select(async content => await ProcessSingleContentAsync(content)) ) ); } private async Task ProcessSingleContentAsync(HttpContent content) { return SomeLogic(await content.ReadAsByteArrayAsync()); } 

    dove SomeLogic è solo una funzione sincrona che prende il contenuto binario e produce una stringa (può essere qualsiasi tipo di elaborazione).

    AGGIORNAMENTO E alla fine ho trovato la spiegazione in questo articolo: https://msdn.microsoft.com/en-us/magazine/jj991977.aspx

    La causa principale di questo deadlock è dovuta al modo in cui attendono i contesti delle maniglie. Per impostazione predefinita, quando è in attesa di un’attività incompleta, il “contesto” corrente viene catturato e utilizzato per riprendere il metodo al termine dell’attività. Questo “contesto” è l’attuale SynchronizationContext a meno che non sia null, nel qual caso è l’attuale TaskScheduler. Le applicazioni GUI e ASP.NET dispongono di un SynchronizationContext che consente l’esecuzione di una sola porzione di codice alla volta. Quando l’attesa è completata, tenta di eseguire il resto del metodo asincrono all’interno del contesto catturato. Ma quel contesto ha già un thread in esso, che è (in modo sincrono) in attesa del completamento del metodo async. Stanno entrambi aspettando l’altro, causando una situazione di stallo.

    Quindi, fondamentalmente, la linea guida “Async all the way” ha una ragione dietro a questo, e questo è un buon esempio.

    Con l’aiuto di un’altra risposta su StackOverflow e un post sul blog su targetFramework , ho riscontrato che l’aggiornamento alla 4.5 e l’aggiunta / aggiornamento di quanto segue nel proprio web.config risolve questo problema:

           

    Ho un progetto .Net MVC WebAPi funzionante con il seguente metodo Post che sembra funzionare bene. È molto simile a quello che hai già, quindi questo dovrebbe essere utile.

      [System.Web.Http.AcceptVerbs("Post")] [System.Web.Http.HttpPost] public Task Post() { // Check if the request contains multipart/form-data. if (!Request.Content.IsMimeMultipartContent()) { throw new HttpResponseException(HttpStatusCode.UnsupportedMediaType); } string fileSaveLocation = @"c:\SaveYourFile\Here\XXX"; CustomMultipartFormDataStreamProvider provider = new CustomMultipartFormDataStreamProvider(fileSaveLocation); Task task = Request.Content.ReadAsMultipartAsync(provider).ContinueWith(t => { if (t.IsFaulted || t.IsCanceled) { Request.CreateErrorResponse(HttpStatusCode.InternalServerError, t.Exception); } foreach (MultipartFileData file in provider.FileData) { //Do Work Here } return Request.CreateResponse(HttpStatusCode.OK); } ); return task; } 

    Ho avuto lo stesso. La mia soluzione

     public List UploadFiles(HttpFileCollection fileCollection) { var uploadsDirectoryPath = HttpContext.Current.Server.MapPath("~/Uploads"); if (!Directory.Exists(uploadsDirectoryPath)) Directory.CreateDirectory(uploadsDirectoryPath); var filePaths = new List(); for (var index = 0; index < fileCollection.Count; index++) { var path = Path.Combine(uploadsDirectoryPath, Guid.NewGuid().ToString()); fileCollection[index].SaveAs(path); filePaths.Add(path); } return filePaths; } 

    e invocando

     if (!Request.Content.IsMimeMultipartContent()) { throw new HttpResponseException(HttpStatusCode.UnsupportedMediaType); } var filePaths = _formsService.UploadFiles(HttpContext.Current.Request.Files);