Avviare un Download con richiesta di nome file da una pagina ASP.NET

Normalmente quando un visitatore clicca su un link che punta a un file, quest’ultimo viene mostrato nel browser e non viene avviato alcun download. Questo succede perché il browser determina il tipo di contenuto in base alla estensione del file. Quando clicchiamo su un file con estensione jpg il browser sa che si tratta di una immagine e la mostra direttamente. Lo stesso avviene per i file pdf e così via.

Può capitare che questo comportamento non sia quello desiderato. Oggi al lavoro ho implementato una funzionalità interessante: il download della versione PDF di una pagina web. Il pdf viene costruito dinamicamente nel momento stesso in cui il visitatore clicca un link. Questo accorgimento libera il webmaster dalla tediosa (e facilmente dimenticabile) necessità di rigenerare il pdf ad ogni minimo ritocco della pagina.

Generazione dinamica del PDF della pagina web

Il visitatore clicca sul link e ottiene il PDF della pagina che sta guardando

Volevo che cliccando sul link si avviasse automaticamente il download, così da ‘costringere’ l’utente a interagire col PDF in un secondo tempo rispetto all’esperienza di navigazione. Se scarichi un PDF è per stamparlo o consultarlo con calma o magari passarlo a un collega, no?

Costringere l’utente a usare il menu contestuale del browser (Salva link con nome..) non è certo la scelta ideale dal punto di vista dell’esperienza utente, tanto più che in questo caso il link non è statico e invoca codice JavaScript. Per ottenere il risultato desiderato dobbiamo inviare direttamente il file all’utente. [cci_asp theme=”standard”]Response.TransmitFile()[/cci_asp] è il metodo che invia il file dall’applicazione ASP.NET al client, ma prima dobbiamo preoccuparci di aggiungere un paio di header al nostro messaggio di output:

Public Sub SendFile(ByVal filename As String)
        'Author: Nicola Iarocci http://nicolaiarocci.com
        Response.Buffer = True
        Response.Charset = ""
        Response.Cache.SetCacheability(HttpCacheability.NoCache)
        Response.ContentType = "application/pdf"
        Response.AddHeader("content-disposition", "attachment;filename=" & Path.GetFileName(filename))
        Response.TransmitFile(filename)
        Response.End()
    End Sub

E’ importante fornire il ContentType corretto altrimenti il browser non sarà sempre in grado di gestire i contenuti del file. Internet Explorer per esempio offre l’opzione di aprire direttamente il file (verrà scaricato nella cache). Nel nostro caso se l’utente sceglie l’apertura diretta IE lancerà Acrobat Reader perché ContentType è application/pdf.

Altro accorgimento è la chiamata al metodo Path.GetFileName() in riga 7. Non sarebbe necessario se non fosse che taluni broswer arcaici e superati (ogni riferimento a IE è naturalmente voluto) non sono in grado di gestire intelligentemente il filename e, quando propongono il nome del file da salvare, riproducono l’intero path del file sul server. Questo comportamento tra l’altro è pericoloso perché espone l’intera struttura  del sito, dalla radice al folder che contiene la pagina, al visitatore.

Se la routine deve essere usata in un modulo e non direttamente in una pagina aspx ricordate di sostituire Response con HttpContect.Current.Response. Buon lavoro!