从spring控制器下载文件

时间:2021-11-02 09:55:35

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 a PDF generation framework like iText. Any better way?

我有一个需要从网站下载PDF的要求。PDF需要在代码中生成,我认为它是freemarker和像iText这样的PDF生成框架的组合。有更好的方法吗?

However, my main problem is how do I allow the user to download a file through a Spring Controller?

但是,我的主要问题是如何允许用户通过Spring控制器下载文件?

9 个解决方案

#1


331  

@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.getOutputStream()时,您可以在其中编写任何内容。您可以将输出流作为放置生成的PDF的地方传递给您的生成器。另外,如果知道要发送什么文件类型,可以设置

response.setContentType("application/pdf");

#2


262  

I was able to stream line this by using the built in support in Spring with it's ResourceHttpMessageConverter. This will set the content-length and content-type if it can determine the mime-type

通过使用Spring中的内置支持,以及ResourceHttpMessageConverter,我能够对其进行流处理。这将设置内容长度和内容类型,如果它可以确定mime类型。

@RequestMapping(value = "/files/{file_name}", method = RequestMethod.GET)
@ResponseBody
public FileSystemResource getFile(@PathVariable("file_name") String fileName) {
    return new FileSystemResource(myService.getFileFor(fileName)); 
}

#3


68  

You should be able to write the file on the response directly. Something like

您应该能够直接在响应上写入文件。类似的

response.setContentType("application/pdf");      
response.setHeader("Content-Disposition", "attachment; filename=\"somefile.pdf\""); 

and then write the file as a binary stream on response.getOutputStream(). Remember to do response.flush() at the end and that should do it.

然后在response.getOutputStream()中将文件写成二进制流。记得要做response.flush()在最后,这应该做。

#4


60  

With Spring 3.0 you can use the HttpEntity return object. If you use this, then your controller does not need a HttpServletResponse object, and therefore it is easier to test. Except this, this answer is relative equals to the one of Infeligo.

使用Spring 3.0,您可以使用HttpEntity返回对象。如果您使用这个,那么您的控制器不需要HttpServletResponse对象,因此测试起来更容易。除了这个,这个答案相对等于不幸福。

If the return value of your pdf framework is an byte array (read the second part of my answer for other return values) :

如果您的pdf框架的返回值是一个字节数组(请阅读我对其他返回值的回答的第二部分):

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

If the return type of your PDF Framework (documentBbody) is not already a byte array (and also no ByteArrayInputStream) then it would been wise NOT to make it a byte array first. Instead it is better to use:

如果您的PDF框架(documentBbody)的返回类型不是一个字节数组(也没有ByteArrayInputStream),那么明智的做法是不要首先将它设置为一个字节数组。相反,最好使用:

example with FileSystemResource:

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

#5


47  

If you:

如果你:

  • Don't want to load the whole file into a byte[] before sending to the response;
  • 不希望在发送到响应之前将整个文件加载到一个字节[]中;
  • Want/need to send/download it via InputStream;
  • 希望/需要通过InputStream发送/下载;
  • Want to have full control of the Mime Type and file name sent;
  • 希望完全控制发送的Mime类型和文件名;
  • Have other @ControllerAdvice picking up exceptions for you.
  • 让其他@ControllerAdvice为您挑选异常。

The code below is what you need:

下面的代码是您需要的:

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

Also notice that to avoid reading the whole file just to calculate its length, you'd better have it stored previously. Make sure you check the docs for InputStreamResource.

还要注意,为了避免仅仅为了计算文件的长度而读取整个文件,您最好事先将它存储起来。一定要检查文档中的InputStreamResource。

#6


15  

This code is working fine to download a file automatically from spring controller on clicking a link on jsp.

在单击jsp上的链接时,此代码可以很好地从spring控制器自动下载文件。

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

}

#7


9  

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

#8


5  

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

我能很快想到的是,生成pdf并将其存储在webapp/downloads/< random_filename >中。从代码中获取pdf并使用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";

#9


1  

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

您可以在这里显示PDF或下载它的示例

#1


331  

@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.getOutputStream()时,您可以在其中编写任何内容。您可以将输出流作为放置生成的PDF的地方传递给您的生成器。另外,如果知道要发送什么文件类型,可以设置

response.setContentType("application/pdf");

#2


262  

I was able to stream line this by using the built in support in Spring with it's ResourceHttpMessageConverter. This will set the content-length and content-type if it can determine the mime-type

通过使用Spring中的内置支持,以及ResourceHttpMessageConverter,我能够对其进行流处理。这将设置内容长度和内容类型,如果它可以确定mime类型。

@RequestMapping(value = "/files/{file_name}", method = RequestMethod.GET)
@ResponseBody
public FileSystemResource getFile(@PathVariable("file_name") String fileName) {
    return new FileSystemResource(myService.getFileFor(fileName)); 
}

#3


68  

You should be able to write the file on the response directly. Something like

您应该能够直接在响应上写入文件。类似的

response.setContentType("application/pdf");      
response.setHeader("Content-Disposition", "attachment; filename=\"somefile.pdf\""); 

and then write the file as a binary stream on response.getOutputStream(). Remember to do response.flush() at the end and that should do it.

然后在response.getOutputStream()中将文件写成二进制流。记得要做response.flush()在最后,这应该做。

#4


60  

With Spring 3.0 you can use the HttpEntity return object. If you use this, then your controller does not need a HttpServletResponse object, and therefore it is easier to test. Except this, this answer is relative equals to the one of Infeligo.

使用Spring 3.0,您可以使用HttpEntity返回对象。如果您使用这个,那么您的控制器不需要HttpServletResponse对象,因此测试起来更容易。除了这个,这个答案相对等于不幸福。

If the return value of your pdf framework is an byte array (read the second part of my answer for other return values) :

如果您的pdf框架的返回值是一个字节数组(请阅读我对其他返回值的回答的第二部分):

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

If the return type of your PDF Framework (documentBbody) is not already a byte array (and also no ByteArrayInputStream) then it would been wise NOT to make it a byte array first. Instead it is better to use:

如果您的PDF框架(documentBbody)的返回类型不是一个字节数组(也没有ByteArrayInputStream),那么明智的做法是不要首先将它设置为一个字节数组。相反,最好使用:

example with FileSystemResource:

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

#5


47  

If you:

如果你:

  • Don't want to load the whole file into a byte[] before sending to the response;
  • 不希望在发送到响应之前将整个文件加载到一个字节[]中;
  • Want/need to send/download it via InputStream;
  • 希望/需要通过InputStream发送/下载;
  • Want to have full control of the Mime Type and file name sent;
  • 希望完全控制发送的Mime类型和文件名;
  • Have other @ControllerAdvice picking up exceptions for you.
  • 让其他@ControllerAdvice为您挑选异常。

The code below is what you need:

下面的代码是您需要的:

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

Also notice that to avoid reading the whole file just to calculate its length, you'd better have it stored previously. Make sure you check the docs for InputStreamResource.

还要注意,为了避免仅仅为了计算文件的长度而读取整个文件,您最好事先将它存储起来。一定要检查文档中的InputStreamResource。

#6


15  

This code is working fine to download a file automatically from spring controller on clicking a link on jsp.

在单击jsp上的链接时,此代码可以很好地从spring控制器自动下载文件。

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

}

#7


9  

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

#8


5  

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

我能很快想到的是,生成pdf并将其存储在webapp/downloads/< random_filename >中。从代码中获取pdf并使用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";

#9


1  

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

您可以在这里显示PDF或下载它的示例