java spring - Descargando un archivo desde los controladores de primavera




mvc download (9)

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);
}

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:

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);
}

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)); 
}

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();
    }

}

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";

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());
        }

    }

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 .


(hilo antiguo, pero solo 2 centavos porque ninguno menciona guava u otras libretas y algunos otros detalles)

Si puedes, usa guayaba

Vale la pena señalar la forma de la Guayaba, que simplifica enormemente estos chanchullos:

Uso

Para una lista inmutable

Use la clase copyOf() y copyOf() métodos de fábrica of() y copyOf() (los elementos no pueden ser nulos) :

List<String> il = ImmutableList.of("string", "elements");  // from varargs
List<String> il = ImmutableList.copyOf(aStringArray);      // from array

Para una lista mutable

Utilice la clase Lists y sus métodos de fábrica newArrayList() :

List<String> l1 = Lists.newArrayList(anotherListOrCollection);    // from collection
List<String> l2 = Lists.newArrayList(aStringArray);               // from array
List<String> l3 = Lists.newArrayList("or", "string", "elements"); // from varargs

Tenga en cuenta también los métodos similares para otras estructuras de datos en otras clases, por ejemplo en Sets .

¿Por qué guayaba?

El principal atractivo podría ser reducir el desorden debido a los genéricos para la seguridad de tipos, ya que el uso de los métodos de fábrica de guayaba permite inferir los tipos la mayor parte del tiempo. Sin embargo, este argumento contiene menos agua desde que Java 7 llegó con el nuevo operador de diamante.

Pero no es la única razón (y Java 7 todavía no está en todas partes): la sintaxis abreviada también es muy útil, y los métodos de inicialización, como se vio anteriormente, permiten escribir código más expresivo. Lo haces en una llamada de guava que lleva 2 con las colecciones de Java actuales.

Si no puedes ...

Para una lista inmutable

Utilice la clase Arrays de JDK y su método de fábrica asList() , envuelto con Collections.unmodifiableList() :

List<String> l1 = Collections.unmodifiableList(Arrays.asList(anArrayOfElements));
List<String> l2 = Collections.unmodifiableList(Arrays.asList("element1", "element2"));

Tenga en cuenta que el tipo devuelto para asList() es una List usa una implementación concreta de ArrayList , pero NO es java.util.ArrayList . Es un tipo interno, que emula una ArrayList pero en realidad hace referencia directa a la matriz pasada y la hace "escritura completa" (las modificaciones se reflejan en la matriz).

Prohíbe las modificaciones a través de algunos de los métodos de la API de la List simplemente extendiendo una AbstractList (por lo tanto, no se admite la adición o eliminación de elementos), sin embargo, permite que las llamadas a set() anulen los elementos. Por lo tanto, esta lista no es realmente inmutable y una llamada a asList() debe envolverse con Collections.unmodifiableList() .

Vea el siguiente paso si necesita una lista mutable.

Para una lista mutable

Igual que el anterior, pero envuelto con un java.util.ArrayList real:

List<String> l1  = new ArrayList<String>(Arrays.asList(array));    // Java 1.5 to 1.6
List<String> l1b = new ArrayList<>(Arrays.asList(array));          // Java 1.7+
List<String> l2  = new ArrayList<String>(Arrays.asList("a", "b")); // Java 1.5 to 1.6
List<String> l2b = new ArrayList<>(Arrays.asList("a", "b"));       // Java 1.7+

Para fines educativos: El buen camino manual.

// for Java 1.5+
static <T> List<T> arrayToList(final T[] array) {
  final List<T> l = new ArrayList<T>(array.length);

  for (final T s : array) {
    l.add(s);
  }
  return (l);
}

// for Java < 1.5 (no generics, no compile-time type-safety, boo!)
static List arrayToList(final Object[] array) {
  final List l = new ArrayList(array.length);

  for (int i = 0; i < array.length; i++) {
    l.add(array[i]);
  }
  return (l);
}




java spring file download controller