2010-07-08 15 views
51

C'è un modo per lo streaming di un file utilizzando ASP.NET MVC FileContentResult all'interno del browser con un nome specifico?File di flusso che utilizza ASP.NET MVC FileContentResult in un browser con un nome?

Ho notato che è possibile avere un FileDialog (Apri/Salva) oppure è possibile eseguire lo streaming del file in una finestra del browser, ma poi utilizzerà ActionName quando si tenta di salvare il file.

Ho il seguente scenario:

byte[] contents = DocumentServiceInstance.CreateDocument(orderId, EPrintTypes.Quote); 
result = File(contents, "application/pdf", String.Format("Quote{0}.pdf", orderId)); 

Quando uso questo, posso trasmettere i byte, ma una finestra di dialogo Apri/Salva è dato per l'utente. Mi piacerebbe in realtà lo streaming di questo file in una finestra del browser.

Se utilizzo solo FilePathResult, mostra il file in una finestra del browser, ma quando faccio clic sul pulsante "Salva" per salvare il file in PDF, mi mostra Nome azione come nome del file.

Qualcuno ha riscontrato questo?

risposta

11

Questo potrebbe essere utile per chiunque altro affronta questo problema. Finalmente ho trovato una soluzione. Risulta, anche se usiamo l'inline per "content-disposition" e specificiamo un nome di file, i browser non usano ancora il nome del file. Invece i browser cercano e interpretano il nome del file in base al percorso/URL.

Si può leggere più avanti questo URL: Securly download file inside browser with correct filename

questo mi ha dato l'idea, ho appena creato il mio percorso URL che convertire l'URL e terminare con il nome del file che volevo dare al file . Quindi per es. la mia chiamata al controller originale consisteva semplicemente nel passare l'ID ordine dell'ordine in fase di stampa. Mi aspettavo che il nome del file fosse del formato Ordine {0} .pdf dove {0} è l'ID ordine. Allo stesso modo per le virgolette, volevo il preventivo {0} .pdf.

Nel mio controller, sono andato avanti e ho aggiunto un parametro aggiuntivo per accettare il nome del file. Ho passato il nome file come parametro nel metodo URL.Action.

Ho poi creato un nuovo percorso che avrebbe mappare l'URL al formato: http://localhost/ShoppingCart/PrintQuote/1054/Quote1054.pdf

 

routes.MapRoute("", "{controller}/{action}/{orderId}/{fileName}", 
       new { controller = "ShoppingCart", action = "PrintQuote" } 
       , new string[] { "x.x.x.Controllers" } 
      ); 
 

Questo più o meno risolto il mio problema. Sperando che questo aiuti qualcuno!

Cheerz, Anup

+0

Un hack, ma un hack molto efficace! Grazie! –

+0

Sfortunatamente, ancora necessario con IE 11. Non necessario con Chrome e Firefox. –

79
public ActionResult Index() 
{ 
    byte[] contents = FetchPdfBytes(); 
    return File(contents, "application/pdf", "test.pdf"); 
} 

e per l'apertura del PDF all'interno del browser è necessario impostare l'intestazione Content-Disposition:

public ActionResult Index() 
{ 
    byte[] contents = FetchPdfBytes(); 
    Response.AddHeader("Content-Disposition", "inline; filename=test.pdf"); 
    return File(contents, "application/pdf"); 
} 
+0

Ciao Darin, Questo apre una finestra di dialogo OPEN/SAVE come ho detto. Voglio questo per aprire un file all'interno del browser. –

+1

@Anup, vedere il mio aggiornamento. –

+0

possiamo avere un valore generico invece di "application/pdf"? come se non fossi sicuro del tipo di file. è l'utente che lo ha caricato io al primo posto – neebz

38

In realtà, il modo assolutamente semplice è quello di fare quanto segue ...

byte[] content = your_byte[]; 

FileContentResult result = new FileContentResult(content, "application/octet-stream") 
       { 
        FileDownloadName = "your_file_name" 
       }; 

return result; 
+1

Nah! .. la mia risposta è più semplice, http://stackoverflow.com/a/29392930/193634 –

+2

Concordo se la domanda originale non includeva un array di byte. – azarc3

0
public FileContentResult GetImage(int productId) { 
    Product prod = repository.Products.FirstOrDefault(p => p.ProductID == productId); 
    if (prod != null) { 
     return File(prod.ImageData, prod.ImageMimeType); 
     } else { 
     return null; 
    } 
} 
5

risposte precedenti sono corrette: aggiungendo la riga ...

Response.AddHeader("Content-Disposition", "inline; filename=[filename]"); 

... causerà l'invio di più intestazioni Content-Disposition nel browser.Questo succede b/c FileContentResult applica internamente l'intestazione se lo si fornisce con un nome di file. Una soluzione alternativa, piuttosto semplice, consiste nel creare semplicemente una sottoclasse di FileContentResult e sovrascrivere il suo metodo ExecuteResult(). Ecco un esempio che crea un'istanza di un'istanza della classe System.Net.Mime.ContentDisposition (lo stesso oggetto utilizzato nel FileContentResult implementazione interna) e lo passa nella nuova classe:

public class FileContentResultWithContentDisposition : FileContentResult 
{ 
    private const string ContentDispositionHeaderName = "Content-Disposition"; 

    public FileContentResultWithContentDisposition(byte[] fileContents, string contentType, ContentDisposition contentDisposition) 
     : base(fileContents, contentType) 
    { 
     // check for null or invalid ctor arguments 
     ContentDisposition = contentDisposition; 
    } 

    public ContentDisposition ContentDisposition { get; private set; } 

    public override void ExecuteResult(ControllerContext context) 
    { 
     // check for null or invalid method argument 
     ContentDisposition.FileName = ContentDisposition.FileName ?? FileDownloadName; 
     var response = context.HttpContext.Response; 
     response.ContentType = ContentType; 
     response.AddHeader(ContentDispositionHeaderName, ContentDisposition.ToString()); 
     WriteFile(response); 
    } 
} 

Nella tua Controller, o in una base Controller, è possibile scrivere un semplice aiuto per istanziare un FileContentResultWithContentDisposition e quindi chiamare dal proprio metodo di azione, in questo modo:

protected virtual FileContentResult File(byte[] fileContents, string contentType, ContentDisposition contentDisposition) 
{ 
    var result = new FileContentResultWithContentDisposition(fileContents, contentType, contentDisposition); 
    return result; 
} 

public ActionResult Report() 
{ 
    // get a reference to your document or file 
    // in this example the report exposes properties for 
    // the byte[] data and content-type of the document 
    var report = ... 
    return File(report.Data, report.ContentType, new ContentDisposition { 
     Inline = true, 
     FileName = report.FileName 
    }); 
} 

Ora il file verrà inviato al browser con il nome del file che si sceglie e con un header Content-disposition di " inline; filename = [nomefile] ".

Mi auguro che aiuta!

+0

Sono passato per la prima volta alla classe di helper 'ContentDisposition', solo per rendermi conto che MVC lo stava usando internamente, ma con qualche trucco per gestire correttamente il nome del file utf-8. La classe helper 'ContentDisposition' sbaglia quando deve codificare i valori utf-8. Per ulteriori dettagli, vedere [il mio commento qui] (/ questions/1012437/uses-of-content-disposition-in-un-http-response-header/22221217 # comment57484455_22221217). –

4

Il modo più semplice in assoluto per lo streaming di un file nel browser utilizzando ASP.NET MVC è questo:

public ActionResult DownloadFile() { 
    return File(@"c:\path\to\somefile.pdf", "application/pdf", "Your Filename.pdf"); 
} 

Questo è più facile che il metodo suggerito da @ azarc3 dal momento che non hanno nemmeno bisogno di leggere i byte .

merito va a: http://prideparrot.com/blog/archive/2012/8/uploading_and_returning_files#how_to_return_a_file_as_response

** Modifica **

A quanto pare la mia 'risposta' è la stessa domanda del PO. Ma non sto affrontando il problema che sta avendo. Probabilmente si trattava di un problema con la versione precedente di ASP.NET MVC?

+0

Il problema può essere astratto a 'MVC invia un'intestazione 'content-disposition' con' attachment' disposition quando si specifica un nome file, come ottenerlo come 'inline'? Metti alla prova i tuoi header di risposta della soluzione, normalmente vedrai anche 'attachment'. –