java - from - spring mvc download file controller
Descargando un archivo desde los controladores de primavera (10)
Tengo un requisito donde necesito descargar un PDF del sitio web. El PDF debe generarse dentro del código, que pensé que sería una combinación de freemarker y un marco de generación de PDF como iText. ¿Alguna forma mejor?
Sin embargo, mi problema principal es ¿cómo puedo permitir que el usuario descargue un archivo a través de un Spring Controller?
Con Spring 3.0 puede usar el objeto de retorno HttpEntity
. Si usa esto, entonces su controlador no necesita un objeto HttpServletResponse
, y por lo tanto es más fácil de probar. Excepto esto, esta respuesta es relativa igual a la de Infeligo .
Si el valor de retorno de su marco pdf es una matriz de bytes (lea la segunda parte de mi respuesta para otros valores de retorno) :
@RequestMapping(value = "/files/{fileName}", method = RequestMethod.GET)
public HttpEntity<byte[]> createPdf(
@PathVariable("fileName") String fileName) throws IOException {
byte[] documentBody = this.pdfFramework.createPdf(filename);
HttpHeaders header = new HttpHeaders();
header.setContentType(MediaType.APPLICATION_PDF);
header.set(HttpHeaders.CONTENT_DISPOSITION,
"attachment; filename=" + fileName.replace(" ", "_"));
header.setContentLength(documentBody.length);
return new HttpEntity<byte[]>(documentBody, header);
}
Si el tipo de retorno de PDF Framework ( documentBbody
) no es ya una matriz de bytes (y tampoco tiene ByteArrayInputStream
), entonces sería prudente NO convertirla primero en una matriz de bytes. En su lugar, es mejor usar:
-
InputStreamResource
, -
PathResource
(desde la primavera 4.0) o -
FileSystemResource
,
ejemplo con FileSystemResource
:
@RequestMapping(value = "/files/{fileName}", method = RequestMethod.GET)
public HttpEntity<byte[]> createPdf(
@PathVariable("fileName") String fileName) throws IOException {
File document = this.pdfFramework.createPdf(filename);
HttpHeaders header = new HttpHeaders();
header.setContentType(MediaType.APPLICATION_PDF);
header.set(HttpHeaders.CONTENT_DISPOSITION,
"attachment; filename=" + fileName.replace(" ", "_"));
header.setContentLength(document.length());
return new HttpEntity<byte[]>(new FileSystemResource(document),
header);
}
Debajo del código me funcionó para generar y descargar un archivo de texto.
@RequestMapping(value = "/download", method = RequestMethod.GET)
public ResponseEntity<byte[]> getDownloadData() throws Exception {
String regData = "Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry''s standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.";
byte[] output = regData.getBytes();
HttpHeaders responseHeaders = new HttpHeaders();
responseHeaders.set("charset", "utf-8");
responseHeaders.setContentType(MediaType.valueOf("text/html"));
responseHeaders.setContentLength(output.length);
responseHeaders.set("Content-disposition", "attachment; filename=filename.txt");
return new ResponseEntity<byte[]>(output, responseHeaders, HttpStatus.OK);
}
Debería poder escribir el archivo en la respuesta directamente. Algo como
response.setContentType("application/pdf");
response.setHeader("Content-Disposition", "attachment; filename=/"somefile.pdf/"");
y luego escriba el archivo como una secuencia binaria en response.getOutputStream()
. Recuerda hacer response.flush()
al final y eso debería hacerlo.
Este código funciona bien para descargar un archivo automáticamente desde Spring Controller al hacer clic en un enlace en jsp.
@RequestMapping(value="/downloadLogFile")
public void getLogFile(HttpSession session,HttpServletResponse response) throws Exception {
try {
String filePathToBeServed = //complete file name with path;
File fileToDownload = new File(filePathToBeServed);
InputStream inputStream = new FileInputStream(fileToDownload);
response.setContentType("application/force-download");
response.setHeader("Content-Disposition", "attachment; filename="+fileName+".txt");
IOUtils.copy(inputStream, response.getOutputStream());
response.flushBuffer();
inputStream.close();
} catch (Exception e){
LOGGER.debug("Request could not be completed at this moment. Please try again.");
e.printStackTrace();
}
}
La siguiente solución funciona para mí
@RequestMapping(value="/download")
public void getLogFile(HttpSession session,HttpServletResponse response) throws Exception {
try {
String fileName="archivo demo.pdf";
String filePathToBeServed = "C://software//Tomcat 7.0//tmpFiles//";
File fileToDownload = new File(filePathToBeServed+fileName);
InputStream inputStream = new FileInputStream(fileToDownload);
response.setContentType("application/force-download");
response.setHeader("Content-Disposition", "attachment; filename="+fileName);
IOUtils.copy(inputStream, response.getOutputStream());
response.flushBuffer();
inputStream.close();
} catch (Exception exception){
System.out.println(exception.getMessage());
}
}
Lo que puedo pensar rápidamente es generar el pdf y almacenarlo en la aplicación web / downloads / <RANDOM-FILENAME> .pdf desde el código y enviar un reenvío a este archivo usando HttpServletRequest
request.getRequestDispatcher("/downloads/<RANDOM-FILENAME>.pdf").forward(request, response);
o si puedes configurar tu vista resolver algo así como,
<bean id="pdfViewResolver"
class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="viewClass"
value="org.springframework.web.servlet.view.JstlView" />
<property name="order" value=”2″/>
<property name="prefix" value="/downloads/" />
<property name="suffix" value=".pdf" />
</bean>
entonces solo regresa
return "RANDOM-FILENAME";
Si tu:
- No desea cargar todo el archivo en un
byte[]
antes de enviarlo a la respuesta; - Quiere / necesita enviarlo / descargarlo a través de
InputStream
; - Quiere tener el control total del tipo de Mime y el nombre del archivo enviado;
- Tenga otro
@ControllerAdvice
recogiendo excepciones para usted.
El siguiente código es lo que necesitas:
@RequestMapping(value = "/stuff/{stuffId}", method = RequestMethod.GET)
public ResponseEntity<InputStreamResource> downloadStuff(@PathVariable int stuffId)
throws IOException {
String fullPath = stuffService.figureOutFileNameFor(stuffId);
File file = new File(fullPath);
HttpHeaders respHeaders = new HttpHeaders();
respHeaders.setContentType("application/pdf");
respHeaders.setContentLength(12345678);
respHeaders.setContentDispositionFormData("attachment", "fileNameIwant.pdf");
InputStreamResource isr = new InputStreamResource(new FileInputStream(file));
return new ResponseEntity<InputStreamResource>(isr, respHeaders, HttpStatus.OK);
}
También tenga en cuenta que para evitar leer todo el archivo solo para calcular su longitud, es mejor tenerlo almacenado previamente. Asegúrese de verificar los documentos para InputStreamResource
.
Tuve la oportunidad de transmitir esto utilizando el soporte integrado en Spring con ResourceHttpMessageConverter. Esto establecerá la longitud del contenido y el tipo de contenido si puede determinar el tipo mime
@RequestMapping(value = "/files/{file_name}", method = RequestMethod.GET)
@ResponseBody
public FileSystemResource getFile(@PathVariable("file_name") String fileName) {
return new FileSystemResource(myService.getFileFor(fileName));
}
algo como abajo
@RequestMapping(value = "/download", method = RequestMethod.GET)
public void getFile(HttpServletResponse response) {
try {
DefaultResourceLoader loader = new DefaultResourceLoader();
InputStream is = loader.getResource("classpath:META-INF/resources/Accepted.pdf").getInputStream();
IOUtils.copy(is, response.getOutputStream());
response.setHeader("Content-Disposition", "attachment; filename=Accepted.pdf");
response.flushBuffer();
} catch (IOException ex) {
throw new RuntimeException("IOError writing file to output stream");
}
}
Puedes visualizar PDF o descargarlo here ejemplos.
@RequestMapping(value = "/files/{file_name}", method = RequestMethod.GET)
public void getFile(
@PathVariable("file_name") String fileName,
HttpServletResponse response) {
try {
// get your file as InputStream
InputStream is = ...;
// copy it to response''s OutputStream
org.apache.commons.io.IOUtils.copy(is, response.getOutputStream());
response.flushBuffer();
} catch (IOException ex) {
log.info("Error writing file to output stream. Filename was ''{}''", fileName, ex);
throw new RuntimeException("IOError writing file to output stream");
}
}
En general, cuando tienes response.getOutputStream()
, puedes escribir cualquier cosa allí. Puede pasar este flujo de salida como un lugar para colocar el PDF generado en su generador. Además, si sabe qué tipo de archivo está enviando, puede configurar
response.setContentType("application/pdf");