Downloading a file from spring controllers

前端 未结 14 905
渐次进展
渐次进展 2020-11-22 01:06

I have a requirement where I need to download a PDF from the website. The PDF needs to be generated within the code, which I thought would be a combination of freemarker and

相关标签:
14条回答
  • 2020-11-22 01:38

    something like below

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

    You can display PDF or download it examples here

    0 讨论(0)
  • 2020-11-22 01:42

    TL;DR

    1. Return ResponseEntity<Resource> from a handler method
    2. Specify Content-Type explicitly
    3. Set Content-Disposition if necessary:
      1. filename
      2. type
        1. inline to force preview in a browser
        2. attachment to force a download
    @Controller
    public class DownloadController {
        @GetMapping("/downloadPdf.pdf")
        // 1.
        public ResponseEntity<Resource> downloadPdf() {
            FileSystemResource resource = new FileSystemResource("/home/caco3/Downloads/JMC_Tutorial.pdf");
            // 2.
            MediaType mediaType = MediaTypeFactory
                    .getMediaType(resource)
                    .orElse(MediaType.APPLICATION_OCTET_STREAM);
            HttpHeaders headers = new HttpHeaders();
            headers.setContentType(mediaType);
            // 3
            ContentDisposition disposition = ContentDisposition
                    // 3.2
                    .inline() // or .attachment()
                    // 3.1
                    .filename(resource.getFilename())
                    .build();
            headers.setContentDisposition(disposition);
            return new ResponseEntity<>(resource, headers, HttpStatus.OK);
        }
    }
    

    Explanation

    Return ResponseEntity<Resouce>

    When you return a ResponseEntity<Resource>, the ResourceHttpMessageConverter kicks in and writes an appropriate response.

    Be aware of possibly wrong Content-Type header set (see FileSystemResource is returned with content type json). That's why this answer suggests setting the Content-Type explicitly.

    Specify Content-Type explicitly:

    Some options are:

    • hardcode the header
    • use the MediaTypeFactory from Spring.
    • or rely on third party library like Apache Tika

    The MediaTypeFactory allows to discover the MediaType appropriate for the Resource (see also /org/springframework/http/mime.types file)

    Set Content-Disposition if necessary:

    Sometimes it is necessary to force a download in a browser or to make the browser open a file as a preview. You can use the Content-Disposition header to satisfy this requirement:

    The first parameter in the HTTP context is either inline (default value, indicating it can be displayed inside the Web page, or as the Web page) or attachment (indicating it should be downloaded; most browsers presenting a 'Save as' dialog, prefilled with the value of the filename parameters if present).

    In the Spring Framework a ContentDisposition can be used.

    To preview a file in a browser:

    ContentDisposition disposition = ContentDisposition
            .builder("inline") // Or .inline() if you're on Spring MVC 5.3+
            .filename(resource.getFilename())
            .build();
    

    To force a download:

    ContentDisposition disposition = ContentDisposition
            .builder("attachment") // Or .attachment() if you're on Spring MVC 5.3+
            .filename(resource.getFilename())
            .build();
    

    Use InputStreamResource carefully:

    Since an InputStream can be read only once, Spring won't write Content-Length header if you return an InputStreamResource (here is a snippet of code from ResourceHttpMessageConverter):

    @Override
    protected Long getContentLength(Resource resource, @Nullable MediaType contentType) throws IOException {
        // Don't try to determine contentLength on InputStreamResource - cannot be read afterwards...
        // Note: custom InputStreamResource subclasses could provide a pre-calculated content length!
        if (InputStreamResource.class == resource.getClass()) {
            return null;
        }
        long contentLength = resource.contentLength();
        return (contentLength < 0 ? null : contentLength);
    }
    

    In other cases it works fine:

    ~ $ curl -I localhost:8080/downloadPdf.pdf  | grep "Content-Length"
    Content-Length: 7554270
    
    0 讨论(0)
  • 2020-11-22 01:43

    What I can quickly think of is, generate the pdf and store it in webapp/downloads/< RANDOM-FILENAME>.pdf from the code and send a forward to this file using HttpServletRequest

    request.getRequestDispatcher("/downloads/<RANDOM-FILENAME>.pdf").forward(request, response);
    

    or if you can configure your view resolver something like,

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

    then just return

    return "RANDOM-FILENAME";
    
    0 讨论(0)
  • 2020-11-22 01:45

    If it helps anyone. You can do what the accepted answer by Infeligo has suggested but just put this extra bit in the code for a forced download.

    response.setContentType("application/force-download");
    
    0 讨论(0)
  • 2020-11-22 01:46
    @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");
        }
    
    }
    

    Generally speaking, when you have response.getOutputStream(), you can write anything there. You can pass this output stream as a place to put generated PDF to your generator. Also, if you know what file type you are sending, you can set

    response.setContentType("application/pdf");
    
    0 讨论(0)
  • 2020-11-22 01:46

    Below code worked for me to generate and download a text file.

    @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);
    }
    
    0 讨论(0)
提交回复
热议问题