【问题标题】:Spring MVC: wrong encoding of downloaded fileSpring MVC:下载文件的错误编码
【发布时间】:2015-04-19 14:04:44
【问题描述】:

我试图了解使用 spring mvc servlet 下载 pdf 文件时的奇怪行为。

这是用于下载文件的控制器代码:

@RequestMapping(value = "/handler/{id}", method = RequestMethod.GET)
public HttpEntity<byte[]> report(@PathVariable("id") Long id, 
        HttpServletResponse response,
        HttpServletRequest request) {
    byte[] bytes = service.reportById(id);

    return DownloadUtil.downloadFile(response, "application/pdf",
           "Filename.pdf", bytes);
}

public static HttpEntity<byte[]> downloadFile(
    final HttpServletResponse response, 
    final String contentType, 
    final String fileName, 
    final byte[] item){

    HttpHeaders header = new HttpHeaders();
    header.setContentType(MediaType.valueOf(contentType));
    header.set("Content-Disposition", "inline; filename=\"" + fileName +"\"");
    header.set("Content-Transfer-Encoding", "application/octet-stream");  

    header.setContentLength(item.length);
    return new HttpEntity<byte[]>(item, header);
}   

当 pdf 在 chrome pdf 查看器中显示时,它可以工作。这里请求/响应标头:

请求:

GET /path/19649/download HTTP/1.1
Host: localhost:8080
Connection: keep-alive
Cache-Control: max-age=0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2272.76 Safari/537.36
Referer: http://path/byDitta
Accept-Encoding: gzip, deflate, sdch
Accept-Language: it,en-US;q=0.8,en;q=0.6
Cookie: JSESSIONID=09CEA1438ACED879CDD96877BB536022; _ga=GA1.1.2013320496.1416898514

回应:

HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
X-Content-Type-Options: nosniff
X-XSS-Protection: 1; mode=block
Cache-Control: no-cache, no-store, max-age=0, must-revalidate
Pragma: no-cache
Expires: 0
X-Frame-Options: DENY
Content-Disposition: inline; filename="file.pdf"
Content-Transfer-Encoding: application/octet-stream
Content-Type: application/pdf;charset=UTF-8
Content-Length: 296750
Date: Tue, 10 Mar 2015 09:39:05 GMT 

当我保存在 pdf 查看器中显示的 pdf 时,我有这个请求/响应标头:

请求:

GET /path/19649/download HTTP/1.1
Host: localhost:8080
Connection: keep-alive
Referer: /path/19649/download
User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2272.76 Safari/537.36
Accept-Encoding: gzip, deflate, sdch
Accept-Language: it,en-US;q=0.8,en;q=0.6
Cookie: JSESSIONID=09CEA1438ACED879CDD96877BB536022; _ga=GA1.1.2013320496.1416898514

回应:

HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
X-Content-Type-Options: nosniff
X-XSS-Protection: 1; mode=block
Cache-Control: no-cache, no-store, max-age=0, must-revalidate
Pragma: no-cache
Expires: 0
X-Frame-Options: DENY
Content-Disposition: inline; filename="file.pdf"
Content-Transfer-Encoding: application/octet-stream
Content-Type: application/pdf;charset=UTF-8
Content-Length: 296750
Date: Tue, 10 Mar 2015 09:40:29 GMT

我认为区别在于accept 请求标头。保存 pdf 请求中缺少它。

问题是,当这个文件被保存时,它有一个错误的编码,因此会被损坏。

奇怪的是我在另一个项目中使用〜相同的代码来做同样的事情并且它可以工作。所以我想,可能是 servlet 配置中的东西?

如何强制正确下载编码?

【问题讨论】:

  • 你能告诉我们相应的响应头吗?特别是检查诸如“编码”和“字符集”之类的东西。我的猜测是文件是用gzip压缩的,需要解压。
  • 谢谢@AaronDigulla 我已经为我的问题添加了详细信息。
  • @AaronDigulla 我尝试解压缩但没有成功。比较文件大小,工作pdf是227403字节,另一个是303206字节
  • @gipinani 你试过不同的浏览器了吗?您还可以将您的 Tomcat conf/server.xml 和 JVM 选项与其他服务器中的选项进行比较,以查找 WRT 编码提示的差异
  • application/pdf;charset=UTF-8 没有意义,因为 PDF 是二进制格式。 Content-Transfer-Encoding 不是标准 HTTP 标头,application/octet-stream 是 MIME 类型,而不是编码。也许 Chrome 对这些问题感到困惑。

标签: java spring google-chrome spring-mvc servlets


【解决方案1】:

如果你的pdf文件生成良好,我认为你应该这样尝试:

         @RequestMapping(value = "clients/city")
         @ResponseBody
private OutputStream getCity(HttpServletRequest request,HttpServletResponse   response) throws IOException, JRException {
  String path=request.getRealPath("resources/files");
  createFileService.SpravkaCity(path); 
 //      response.setContentType("text/plain");      
 //      response.setHeader("Content-Disposition", "attachment;   filename=reestr.xls");
  File f=new File(path+"/city.pdf");
  response.setContentType("application/pdf");
 //      response.setHeader("Content-Transfer-Encoding", "binary");
  response.setHeader("Content-Length", String.valueOf(f.length()));
  response.setHeader("Content-Disposition", "inline; filename=city.pdf");
  Path p = Paths.get(path+"/city.pdf");
  response.getOutputStream().write(Files.readAllBytes(p));
  return response.getOutputStream();
} 

希望对你有帮助。

【讨论】:

    【解决方案2】:

    来自评论:

    比较文件大小,工作pdf是227403字节,另一个是303206字节

    这表明数据是Base64 encoded。我不明白为什么会发生这种情况;大多数情况下,当服务器认为客户端无法处理二进制数据时(例如当您执行 AJAX 请求时),您会得到这种效果。

    [编辑] 安装像Fiddler 这样的代理服务器,它允许您查看服务器发送到浏览器的原始数据。尝试只创建一个小的 PDF,以使其更容易。

    使用这些工具,您可以找出谁对数据进行了编码。

    【讨论】:

    • 感谢您的回答。从客户端这不是我的错误,因为我检查了 chrome pdf 查看器的保存按钮。而且我也不明白为什么另一个项目中的相同代码可以工作。例如,Firefox 运行良好,因为当按下保存 pdf 时,它不会发出另一个请求,而是保存 pdf 的本地副本。 Chome打了2个电话。一个用于预览,一个用于保存对象。我认为强制下载应该可以解决问题,但是在查看器中查看 pdf 很舒服
    • Chrome 的第二个请求是否有所不同?
    • 是的,它显示在问题中。也许还不清楚。我向您展示第一个请求和第二个请求,我在其中显示标题
    • 啊,我明白了!您的下一步应该是查看服务器发送到浏览器的原始数据。尝试创建一个较小的 PDF 并寻找可以安装在 PC 上的代理服务器,例如 Fiddler 如果线路上的数据有误,请开始调试服务器。
    • 我尝试了各种组合,我遇到了这个确切的问题,但仅适用于 IE11。你的回答让我开始思考,所以我通过将 .pdf 添加到 rest-call 来解决它 - “myApp/REST/public/v1/downloadPdf.pdf”。难以置信,但它奏效了。真的不需要它。
    猜你喜欢
    • 1970-01-01
    • 2017-07-01
    • 2021-03-07
    • 1970-01-01
    • 2018-02-21
    • 1970-01-01
    • 1970-01-01
    • 2015-08-03
    • 1970-01-01
    相关资源
    最近更新 更多