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

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


当前回答

如果这对谁有帮助的话。你可以按照Infeligo给出的答案去做,但只需要在强制下载的代码中添加额外的内容即可。

response.setContentType("application/force-download");

其他回答

Do

从处理程序方法返回ResponseEntity<Resource> 指定内容类型 如果需要,设置Content-Disposition: 文件名 类型 内联以强制在浏览器中预览 附件强制下载

例子

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

解释

返回ResponseEntity <资源>

当您返回ResponseEntity<Resource>时,ResourceHttpMessageConverter将写入文件内容

资源实现的例子:

基于字节的ByteArrayResource [] FileSystemResource—用于文件或路径 UrlResource -从java.net.URL检索 GridFsResource—存储在MongoDB中的blob ClassPathResource—用于类路径中的文件,例如资源目录中的文件。我对“从Spring Boot中的资源文件夹中读取文件”问题的回答详细解释了如何在类路径中定位资源

显式指定Content-Type:

原因:请参阅“FileSystemResource返回内容类型为json”问题

选项:

硬编码头文件 使用Spring中的MediaTypeFactory。MediaTypeFactory使用/org/springframework/http/mime将Resource映射到MediaType。类型文件 使用第三方库,如Apache Tika

如果需要,设置Content-Disposition:

关于Content-Disposition头:

HTTP上下文中的第一个参数要么是内联(默认值,表明它可以在Web页面中显示,也可以作为Web页面显示),要么是附件(表明应该下载它;大多数浏览器会显示一个“另存为”对话框,如果存在的话,会预先填充文件名参数的值)。

在应用中使用ContentDisposition:

在浏览器中预览文件: 性格=性格 .inline () .filename (resource.getFilename ()) .build (); 强制下载: 性格=性格 .attachment () .filename (resource.getFilename ()) .build ();

小心使用InputStreamResource:

使用HttpHeaders#setContentLength方法指定Content-Length,如果:

长度是已知的 使用InputStreamResource

原因:Spring不会为InputStreamResource写Content-Length,因为Spring不能确定资源的长度。下面是来自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);
}

在其他情况下,Spring设置Content-Length:

~ $ curl -I localhost:8080/downloadPdf.pdf  | grep "Content-Length"
Content-Length: 7554270

我能很快想到的是,生成pdf并将其存储在webapp/downloads/< RANDOM-FILENAME>.pdf代码中,并使用HttpServletRequest发送一个转发到这个文件

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

或者你可以这样配置你的视图解析器,

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

然后返回

return "RANDOM-FILENAME";

我必须加上这个才能下载任何文件

    response.setContentType("application/octet-stream");
    response.setHeader("Content-Disposition",
            "attachment;filename="+"file.txt");

所有的代码:

@Controller
public class FileController {

@RequestMapping(value = "/file", method =RequestMethod.GET)
@ResponseBody
public FileSystemResource getFile(HttpServletResponse response) {

    final File file = new File("file.txt");
    response.setContentType("application/octet-stream");
    response.setHeader("Content-Disposition",
            "attachment;filename="+"file.txt");
    return new FileSystemResource(file);
 }
}

如果这对谁有帮助的话。你可以按照Infeligo给出的答案去做,但只需要在强制下载的代码中添加额外的内容即可。

response.setContentType("application/force-download");
@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");
    }

}

一般来说,当你有response.getOutputStream()时,你可以在那里写任何东西。您可以将此输出流作为将生成的PDF放置到生成器的位置。另外,如果你知道你要发送什么文件类型,你可以设置

response.setContentType("application/pdf");