我一直在努力解决以下问题两天,并且无法理解它。
我正在尝试在Spring Boot rest应用程序中提供静态pdf。它应该非常直接,但我无法让它发挥作用。
首先,我只是将pdf放在资源文件夹中,并尝试直接从javascript代码加载它,如下所示:
var newWindow = window.open(/pdf/test.pdf, '');
这导致了一个新窗口,其中pdf没有显示任何内容。
将pdf从浏览器保存到磁盘并调查内容显示它们与原始文件不同。我正在使用ISO-8859-1编码显示Atom的截图(原始第一个):
我的结论到目前为止:Spring或Tomcat以某种方式改变了二进制内容。也许是编码呢?在Base64?
然后我尝试在服务器端实现它,看看发生了什么。我实现了一个可以提供pdf内容的休息控制器。
一个有趣的发现是它最初给出了与直接方法相同的结果。我使用classPathResource来获取pdf文件的句柄。
但是当我使用FileInputStream和File直接从路径加载pdf时,它可以工作。请参阅以下代码:
@RequestMapping(value = "/test.pdf", method = RequestMethod.GET, produces = "application/pdf")
public void getFile(HttpServletResponse response) {
try {
DefaultResourceLoader loader = new DefaultResourceLoader();
/* does not work
ClassPathResource pdfFile = new ClassPathResource("test.pdf");
InputStream is = pdfFile.getInputStream();
*/
/* works */
InputStream is = new FileInputStream(new File("z:\\downloads\\test.pdf"));
IOUtils.copy(is, response.getOutputStream());
response.setHeader("Content-Disposition", "inline; filename=test.pdf");
response.setContentType("application/pdf");
response.flushBuffer();
} catch (IOException ex) {
throw new RuntimeException("IOError writing file to output stream");
}
}
这里发生了什么?为什么Spring / Tomcat在使用ClassPathResource或直接提供时更改二进制数据?
我很感激这里的一些帮助。我不能使用直接路径,因为pdf最终会在jar文件中,所以我需要ClassPathResource或其他一些ResourceLoader。
答案 0 :(得分:1)
好的,最后我找到了罪魁祸首,这是一个完全意想不到的角落。
我在这个项目中使用IntelliJ和Maven,事实证明,当将PDF文件复制到/ target文件夹时,它会损坏pdf的内容。当然tomcat服务于这个文件,而不是/ src文件夹中的文件...所以它与ClassPathResource或Spring无关。这是Maven。
我不得不在pom.xml中禁用(二进制)pdf的过滤:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-resources-plugin</artifactId>
<configuration>
<nonFilteredFileExtensions>
<nonFilteredFileExtension>pdf</nonFilteredFileExtension>
</nonFilteredFileExtensions>
</configuration>
</plugin>
解决了这个问题。现在直接请求该文件(localhost:8080 / test.pdf)以及其余的控制器方法工作。 @Andy Brown:感谢快速回复,尽管它没有解决问题。
答案 1 :(得分:0)
直接写入响应输出流可能会影响您设置标头的能力。我使用curl
作为用户代理测试了您的代码,并且响应中缺少Content-Type
,这会导致您的客户端应用转换并弄乱内容。
重写你的身体看起来会解决这个问题:
@RequestMapping(value = "/test.pdf", method = RequestMethod.GET, produces = "application/pdf")
public ResponseEntity<InputStreamResource> getFile() {
try {
ClassPathResource pdfFile = new ClassPathResource("test.pdf");
HttpHeaders headers = new HttpHeaders();
headers.add(HttpHeaders.CONTENT_DISPOSITION, "inline; filename=test.pdf");
InputStream is = pdfFile.getInputStream();
return new ResponseEntity<>(
new InputStreamResource(is),
headers,
HttpStatus.OK);
} catch (IOException ex) {
throw new RuntimeException("IOError writing file to output stream");
}
}
我从这样一个事实中得出结论:这些可能很大并且您关注效率。执行此操作的最佳方法是返回StreamingResponseBody
的实现,在该实现中使用快速NIO流到流的副本实现write
方法。