incorrect encoding serving binary (pdf) file through ClassPathResource in Spring

旧城冷巷雨未停 提交于 2021-01-28 11:23:06

问题


I have been struggling with the following problem for two days and can not get my head around it.

I am trying to serve a static pdf in a Spring Boot rest application. It should be very straight forward but I just cannot get it to work.

First I simply placed the pdf in the resource folder and tried to load it directly from the javascript code, like this:

var newWindow = window.open(/pdf/test.pdf, ''); 

That resulted in a new window with a pdf not showing any content.

Saving the pdf to disk from the browser and investigating the contents revealed that they were different from the original. I am showing screenshots from Atom for both (original first), in ISO-8859-1 encoding:

My conclusion so far: Spring or Tomcat somehow changed the binary content. Maybe it is encoding it? In Base64?

Then I tried to implement it on the server side, to see what is going on. I implemented a rest controller that would serve the pdf contents.

An interesting find is that it initially gave the same results as with the direct approach. I used the classPathResource to get a handle to the pdf file.

But when I load the pdf directly from a path using FileInputStream and File, it works. See code below:

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

What is going on here? Why is Spring/Tomcat changing the binary data when using ClassPathResource or when serving it directly?

I would be grateful for some help here. I cannot use a direct path because the pdf will be in a jar file eventually, so I will need ClassPathResource or some other ResourceLoader.


回答1:


Ok, finally I found the culprit and it was in a completely unexpected corner.

I am using IntelliJ with Maven for this project and as it turns out, IntelliJ corrupted the content of the pdf when copying it to the /target folder. And of course tomcat was serving this file, not the one in the /src folder... so it had nothing to do with ClassPathResource or Spring. It was Maven.

I had to disable filtering for (binary) pdf in the pom.xml:

        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-resources-plugin</artifactId>
            <configuration>
                <nonFilteredFileExtensions>
                    <nonFilteredFileExtension>pdf</nonFilteredFileExtension>
                </nonFilteredFileExtensions>
            </configuration>
        </plugin>

which solved the issue. Now a direct request of the file (localhost:8080/test.pdf) as well as the rest controller approach work. @Andy Brown: thanks for the quick reply although it didn't solve the issue.




回答2:


Writing directly to the response output stream might be nuking your ability to set headers. I tested your code with curl as the user agent and Content-Type was missing from the response which would cause your client to apply a transformation and mess up the content.

Rewriting your body to look like this will fix the problem:

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

I get from the fact that you're streaming responses that these are potentially large and you care about efficiency. The best way to do this is to return an implementation of StreamingResponseBody in which you implement the write method using a fast NIO stream-to-stream copy.



来源:https://stackoverflow.com/questions/49294378/incorrect-encoding-serving-binary-pdf-file-through-classpathresource-in-spring

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!