Ho un'azione Struts2 nel lato server per il download di file.
<action name="download" class="com.xxx.DownAction">
<result name="success" type="stream">
<param name="contentType">text/plain</param>
<param name="inputName">imageStream</param>
<param name="contentDisposition">attachment;filename={fileName}</param>
<param name="bufferSize">1024</param>
</result>
</action>
Tuttavia quando chiamo l'azione usando il jQuery:
$.post(
"/download.action",{
para1:value1,
para2:value2
....
},function(data){
console.info(data);
}
);
in Firebug vedo che i dati vengono recuperati con il flusso binario. Mi chiedo come aprire la finestra di download del file con cui l'utente può salvare il file localmente?
2019 aggiornamento dei browser moderni
Questo è l'approccio che raccomando ora, con alcune avvertenze:
fetch('https://jsonplaceholder.typicode.com/todos/1')
.then(resp => resp.blob())
.then(blob => {
const url = window.URL.createObjectURL(blob);
const a = document.createElement('a');
a.style.display = 'none';
a.href = url;
// the filename you want
a.download = 'todo-1.json';
document.body.appendChild(a);
a.click();
window.URL.revokeObjectURL(url);
alert('your file has downloaded!'); // or you know, something with better UX...
})
.catch(() => alert('oh no!'));
2012 Approccio originale basato su jQuery/iframe/cookie
Bluish ha completamente ragione su questo, non puoi farlo tramite Ajax perché JavaScript non può salvare i file direttamente sul computer di un utente (per questioni di sicurezza). Sfortunatamente puntare l'URL della *finestra principale'al tuo download di file significa che hai poco controllo su quella che è l'esperienza dell'utente quando si verifica un download di file.
Ho creato jQuery File Download che permette un'esperienza "Ajax like" con i download di file completa di callback OnSuccess e OnFailure per fornire una migliore esperienza all'utente. Date un'occhiata al mio post sul blog sul problema comune che il plugin risolve e alcuni modi per usarlo e anche una demo di jQuery File Download in azione. Ecco la fonte
Ecco un semplice caso d'uso demo che usa il plugin source con le promesse. La pagina demo include anche molti altri esempi, 'better UX'.
$.fileDownload('some/file.pdf')
.done(function () { alert('File download a success!'); })
.fail(function () { alert('File download failed!'); });
A seconda dei browser che devi supportare potresti essere in grado di usare https://github.com/eligrey/FileSaver.js/ che permette un controllo più esplicito del metodo IFRAME che jQuery File Download usa.
*Nessuno ha postato questa soluzione di @Pekka... quindi la posterò io. Può aiutare qualcuno.
Non hai bisogno di farlo tramite Ajax. Basta usare
window.location="download.action?para1=value1...."
1. Framework agnostico: Servlet che scarica il file come allegato
<!-- with JS -->
<a href="javascript:window.location='downloadServlet?param1=value1'">
download
</a>
<!-- without JS -->
<a href="downloadServlet?param1=value1" >download</a>
2. Framework Struts2: Azione che scarica il file come allegato
<!-- with JS -->
<a href="javascript:window.location='downloadAction.action?param1=value1'">
download
</a>
<!-- without JS -->
<a href="downloadAction.action?param1=value1" >download</a>
Sarebbe meglio usare il tag <s:a>
che punta con OGNL ad una URL creata con il tag <s:url>
:
<!-- without JS, with Struts tags: THE RIGHT WAY -->
<s:url action="downloadAction.action" var="url">
<s:param name="param1">value1</s:param>
</s:ulr>
<s:a href="%{url}" >download</s:a>
Nei casi precedenti, è necessario scrivere l'intestazione Content-Disposition nella risposta, specificando che il file deve essere scaricato (attachment
) e non aperto dal browser (inline
). È necessario specificare anche il Tipo di contenuto, e potresti voler aggiungere il nome del file e la lunghezza (per aiutare il browser a disegnare una barra di avanzamento realistica).
Per esempio, quando si scarica uno ZIP:
response.setContentType("application/zip");
response.addHeader("Content-Disposition",
"attachment; filename=\"name of my file.zip\"");
response.setHeader("Content-Length", myFile.length()); // or myByte[].length...
Con Struts2 (a meno che non si stia usando l'azione come Servlet, un hack per lo streaming diretto, per esempio), non c'è bisogno di scrivere direttamente qualcosa nella risposta; semplicemente usando il tipo di risultato Stream e configurandolo in struts.xml funzionerà: ESEMPIO
<result name="success" type="stream">
<param name="contentType">application/zip</param>
<param name="contentDisposition">attachment;filename="${fileName}"</param>
<param name="contentLength">${fileLength}</param>
</result>
3. Framework agnostico (/ framework Struts2): Servlet(/Action) che apre il file all'interno del browser
Se si vuole aprire il file all'interno del browser, invece di scaricarlo, la Content-disposition deve essere impostata su inline, ma il target non può essere la posizione attuale della finestra; si deve puntare a una nuova finestra creata da javascript, un <iframe>
nella pagina, o una nuova finestra creata al volo con il "discussed" target="_blank":
<!-- From a parent page into an IFrame without javascript -->
<a href="downloadServlet?param1=value1" target="iFrameName">
download
</a>
<!-- In a new window without javascript -->
<a href="downloadServlet?param1=value1" target="_blank">
download
</a>
<!-- In a new window with javascript -->
<a href="javascript:window.open('downloadServlet?param1=value1');" >
download
</a>