2015-05-14 9 views
6

Sto cercando di rendere un file ASP.Net 5 MVC 6 WebAPI un file, in risposta a una richiesta HttpGET.MVC 6 WebAPI restituisce HttpResponseMessage serializzato anziché il flusso di file

Il file proviene da una condivisione di file di Azure, ma potrebbe essere qualsiasi flusso contenente un file binario.

Mi sembra che MVC serializzi l'oggetto risposta e restituisca il JSON risultante, anziché restituire l'oggetto risposta stesso.

Ecco il mio metodo di controllo:

[HttpGet] 
    [Route("GetFile")] 
    public HttpResponseMessage GetFile(string Username, string Password, string FullName) 
    { 

     var client = new AzureFilesClient.AzureFilesClient(Username, Password); 
     Stream azureFileStream = client.GetFileStream(FullName).Result; 
     var fileName = Path.GetFileName(FullName); 

     using (HttpResponseMessage response = new HttpResponseMessage(HttpStatusCode.OK)) 
     { 
      response.Content = new StreamContent(azureFileStream); 
      response.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream"); 
      response.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment") { FileName = fileName }; 
      return response; 
     } 
    } 

Il metodo GetFileStream sul AzureFilesClient è qui, anche se la fonte di flusso potrebbe essere qualsiasi cosa che contiene il contenuto del file binario:

public async Task<Stream> GetFileStream(string fileName) 
    { 
     var uri = new Uri(share.Uri + "/" + fileName); 
     var file = new CloudFile(uri, credentials); 

     using (var stream = new MemoryStream()) 
     { 
      await file.DownloadToStreamAsync(stream); 
      stream.Seek(0, SeekOrigin.Begin); 
      return stream; 
     } 
    } 

Edit: Ecco un esempio della risposta JSON:

{ 
    "Version": { 
     "Major": 1, 
     "Minor": 1, 
     "Build": -1, 
     "Revision": -1, 
     "MajorRevision": -1, 
     "MinorRevision": -1 
    }, 
    "Content": { 
     "Headers": [ 
      { 
       "Key": "Content-Type", 
       "Value": [ 
        "application/octet-stream" 
       ] 
      }, 
      { 
       "Key": "Content-Disposition", 
       "Value": [ 
        "attachmentx; filename=\"samplefile.docx\"" 
       ] 
      } 
     ] 
    }, 
    "StatusCode": 200, 
    "ReasonPhrase": "OK", 
    "Headers": [], 
    "RequestMessage": null, 
    "IsSuccessStatusCode": true 
} 

risposta

6

Dopo una combinazione di rea documentazione e qualche prova ed errore, i problemi sono stati risolti.

La parte Azure è stata effettuata utilizzando il pacchetto Nuget "WindowsAzure.Storage" (4.4.1-preview)

Prima l'uscita che ha ottenuto JSON serializzato. Ciò ha richiesto invece un risultato di azione personalizzato da restituire.

using Microsoft.AspNet.Mvc; 
using System.IO; 
using System.Threading.Tasks; 

public class FileResultFromStream : ActionResult 
{ 
    public FileResultFromStream(string fileDownloadName, Stream fileStream, string contentType) 
    { 
     FileDownloadName = fileDownloadName; 
     FileStream = fileStream; 
     ContentType = contentType; 
    } 

    public string ContentType { get; private set; } 
    public string FileDownloadName { get; private set; } 
    public Stream FileStream { get; private set; } 

    public async override Task ExecuteResultAsync(ActionContext context) 
    { 
     var response = context.HttpContext.Response; 
     response.ContentType = ContentType; 
     context.HttpContext.Response.Headers.Add("Content-Disposition", new[] { "attachment; filename=" + FileDownloadName }); 
     await FileStream.CopyToAsync(context.HttpContext.Response.Body); 
    } 
} 

Ora, per ottenere i dati binari in streaming da un file di condivisione Azure (o qualsiasi altra fonte di flusso asincrona)

using Microsoft.WindowsAzure.Storage; 
using Microsoft.WindowsAzure.Storage.Auth; 
using Microsoft.WindowsAzure.Storage.File; 
using System; 
using System.Collections.Generic; 
using System.IO; 
using System.Text; 
using System.Threading.Tasks; 

    public async Task<Stream> GetFileStreamAsync(string fileName) 
    { 
     var uri = new Uri(share.Uri + "/" + fileName); 
     var file = new CloudFile(uri, credentials); 

     // Note: Do not wrap the stream variable in a Using, since it will close the stream too soon. 
     var stream = new MemoryStream(); 
     await file.DownloadToStreamAsync(stream); 
     stream.Seek(0, SeekOrigin.Begin); 
     return stream; 
    } 

E infine il codice del controller. Notare l'uso dell'interfaccia IActionResult.

[HttpGet] 
    [Route("GetFile")] 
    public async Task<IActionResult> GetFile(string username, string password, string fullName) 
    { 
     var client = new AzureFilesClient.AzureFilesClient(username, password); 
     Stream stream = await client.GetFileStreamAsync(fullName); 
     string fileName = Path.GetFileName(fullName); 
     return new CustomActionResults.FileResultFromStream(fileName, stream, "application/msword"); 
    } 

Nota: Questo esempio viene usato solo con i file di Word, si potrebbe voler esaminare rendere il parametro ContentType dinamica, invece di statico come questo.

+1

Buona risposta, e funziona ancora. Il team AspNet ha implementato la propria soluzione FileStreamResult: FileResult: ActionResult, ma non lo ha reso disponibile nello spazio dei nomi System.Net.Http utilizzato da ApiController. Quindi ecco la fonte se ti piacerebbe usarlo: http://aspnetwebstack.codeplex.com/SourceControl/latest#src/System.Web.Mvc/FileResult.cs – OzBob

+0

Questa è la soluzione che funziona bene per me, ma io Sono ancora confuso su ciò che alla fine chiude il flusso. Viene appena eliminato dalla garbage collection? O dovrebbe essere chiuso alla fine del metodo ExecuteResultAsync? –

+0

Come hai trovato le prestazioni con file di grandi dimensioni (~ 500mb)? Sto riscontrando problemi con connessioni più lente che scendono dopo ~ 200mb. Le connessioni più veloci non hanno alcun problema. – rolls

Problemi correlati