Tengo una acción de Struts2 en el lado del servidor para la descarga de archivos.
<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>
Sin embargo, cuando llamo a la acción utilizando el jQuery:
$.post(
"/download.action",{
para1:value1,
para2:value2
....
},function(data){
console.info(data);
}
);
en Firebug veo que los datos se recuperan con el stream binario. Me pregunto cómo abrir la ventana de descarga de archivos con la que el usuario puede guardar el archivo localmente.
Actualización de los navegadores modernos de 2009
Este es el enfoque que yo recomendaría ahora con algunas advertencias:
begin snippet: js hide: false console: false babel: false -->
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 Enfoque original basado en jQuery/iframe/Cookie
Bluish tiene toda la razón en esto, no puedes hacerlo a través de Ajax porque JavaScript no puede guardar archivos directamente en el ordenador del usuario (por cuestiones de seguridad). Desgraciadamente, apuntar la URL de la ventana principal a la descarga de archivos significa que tiene poco control sobre la experiencia del usuario cuando se produce una descarga de archivos.
He creado jQuery File Download que permite una experiencia "Ajax like" con descargas de archivos completa con OnSuccess y OnFailure callbacks para proporcionar una mejor experiencia de usuario. Echa un vistazo a mi post del blog sobre el problema común que el plugin resuelve y algunas formas de usarlo y también una demo de jQuery File Download en acción. Aquí está la fuente
Aquí hay una demostración de un caso de uso simple usando el plugin fuente con promesas. La página de demostración incluye muchos otros, 'mejor UX' ejemplos también.
$.fileDownload('some/file.pdf')
.done(function () { alert('File download a success!'); })
.fail(function () { alert('File download failed!'); });
Dependiendo de los navegadores que necesites soportar puedes usar https://github.com/eligrey/FileSaver.js/ que permite un control más explícito que el método IFRAME que usa jQuery File Download.
Nadie ha publicado esto @Pekka's solution... así que lo publicaré. Puede ayudar a alguien.
No es necesario hacer esto a través de Ajax. Simplemente usa
window.location="download.action?para1=value1...."
1. Agnóstico del marco de trabajo: Servlet que descarga el archivo como adjunto.
<!-- with JS -->
<a href="javascript:window.location='downloadServlet?param1=value1'">
download
</a>
<!-- without JS -->
<a href="downloadServlet?param1=value1" >download</a>
2. Struts2 Framework: Acción de descarga de archivo como archivo adjunto
<!-- with JS -->
<a href="javascript:window.location='downloadAction.action?param1=value1'">
download
</a>
<!-- without JS -->
<a href="downloadAction.action?param1=value1" >download</a>
Sería mejor utilizar la etiqueta <s:a>
apuntando con OGNL a una URL creada con la etiqueta <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>
En los casos anteriores, usted necesita escribir la cabecera Content-Disposition en la respuesta, especificando que el archivo debe ser descargado (attachment
) y no abierto por el navegador (inline
). También es necesario especificar el Tipo de Contenido, y es posible que quieras añadir el nombre del archivo y su longitud (para ayudar al navegador a dibujar una barra de progreso realista).
Por ejemplo, al descargar un 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 menos que estés usando la acción como un servlet, un hack para streaming directo, por ejemplo), no necesitas escribir directamente nada en la respuesta; simplemente usando el Stream result type y configurándolo en struts.xml funcionará: EJEMPLO
<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. Marco agnóstico (/ marco Struts2): Servlet(/Acción) abriendo archivo dentro del navegador
Si quiere abrir el archivo dentro del navegador, en lugar de descargarlo, la Content-disposition debe establecerse como inline, pero el objetivo no puede ser la ubicación de la ventana actual; debe apuntar a una nueva ventana creada por javascript, un `<iframe>en la página, o una nueva ventana creada sobre la marcha con el "discutido" 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>