【问题标题】:AngularJS POST for PDF data always failsPDF数据的AngularJS POST总是失败
【发布时间】:2015-12-08 00:58:57
【问题描述】:

我正在尝试解决 Internet Explorer 的 URL 限制(有关详细信息,请参阅 this Microsoft support article)以生成 PDF。

我的客户端 JS 中有一个 base-64 编码的字符串,表示 PDF 的字节流。对于 Firefox 和 Chrome,我通过在字符串前面加上 data:application/pdf;base64, 来显示 PDF,浏览器会愉快地呈现数据 URI。

对于 Internet Explorer,我尝试在 POST 中将字符串发送回 servlet,以便它可以返回内容类型为“application/pdf”的字节流。我几乎遵循以下问题中提出的解决方案,除了我在客户端请求中传递实际的 PDF 内容:

我的 HTTP POST 如下所示:

var pdf; // base-64 encoded string in this variable
$http.post("/convert/pdf", {
    params: {
      base64pdf: pdf
    }
  }, {
    responseType : "arraybuffer"
  }).then( function onSuccess(result) { 
             var file = new Blob([result.data], {type: 'application/pdf'});
             window.navigator.msSaveOrOpenBlob(file, "result.pdf");
           },
           function onFailure() { /* log and do failure case stuff */ });

我的服务器正在运行 Java spring-webmvc 服务。 Convert 控制器如下所示:

@Controller
@RequestMapping(value = "/convert", produces = "application/pdf")
public class ConvertController {
    @RequestMapping(value = "/pdf", method = RequestMethod.POST)
    public ResponseEntity<byte[]> generatePdf(
        @RequestParam(value="base64pdf", required=true) String base64encodedDataUri,
        HttpServletRequest request) {
      byte[] bytes = Base64.decodeBase64(base64encodedDataUri);

      HttpHeaders headers = new HttpHeaders();
      headers.setContentType(MediaType.parseMediaType("application/pdf"));

      String filename = "test.pdf";
      headers.setContentDispositionFormData(filename, filename);
      ResponseEntity<byte[]> response = new ResponseEntity<>(bytes, headers, HttpStatus.OK);
      return response;
    }
    // other methods
}

控制器代码基于this answer。我不认为有问题。它通过 Swagger API 接口在单元测试和直接 POST 请求中测试良好。

问题是这样的。每当我在 IE 中触发 $http.post 时,结果是总是错误回调,并且总是是 HTTP 400 错误。

我观察到以下情况:

  • 使用 IE 11 的开发人员工具网络流量捕获,我可以观察到正在发出的请求。请求在参数中包含完整且正确的 base-64 编码字符串,该字符串存在于请求正文中。

  • 请求标头如下所示:

请求 POST /convert/pdf HTTP/1.1
内容类型 application/json;charset=utf-8
接受 application/json, text/plain, */*
引用本地主机:8080/
Accept-Language en-US
接受编码 gzip,放气
用户代理 Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) 像 Gecko
主机本地主机:8080
内容长度 5699
DNT 1
连接保活
缓存连续

滚动无缓存
  • 响应标头显示如下:

响应 HTTP/1.1 400 错误请求
服务器 Apache-Coyote/1.1
访问控制允许来源 *
访问控制允许方法 POST、GET、OPTIONS、DELETE
访问控制最大年龄 3600
Access-Control-Allow-Headers x-requested-with
内容类型应用程序/pdf
内容长度 98
日期 2015 年 12 月 7 日星期一 21:54:32 GMT
连接关闭
  • 响应正文包含无效的 PDF。好吧,从技术上讲,开发者工具说它无法渲染它,当你保存它时,保存的 PDF 不是有效的 PDF。

  • 编辑:我登录 Tomcat8 并观察到 ​​Spring 抱怨请求中缺少“base64pdf”参数:

2015-12-08 02:29:32,514 INFO [http-nio-8080-exec-4] INFO Required String parameter 'base64pdf' is not present
org.springframework.web.bind.MissingServletRequestParameterException: Required String parameter 'base64pdf' is not present

所以我真的有两个问题:

  1. 我怎么会丢失base64pdf 参数? 参数是否有最大长度?我尝试了这个问题中显示的原始内容,并按照下面用户 chenzhenjia 的建议通过data: { base64pdf : pdf } 传递参数。

  2. 我怎样才能让这个 PDF 调用在 IE 中工作? 虽然一方面我想知道我在上面做错了什么,但坦率地说我很开放关于如何将客户端上的这个 base-64 编码字符串转换为 PDF 的“正确答案”。 (也就是说,如果这是XY problem,而我找错了树,我也想知道。)


我正在使用:

  • AngularJS 1.4.8
  • Spring 4.2.3.RELEASE
  • Java 8
  • 雄猫 8

【问题讨论】:

    标签: java angularjs spring-web


    【解决方案1】:

    我最终通过在请求正文中发送 base64pdf 字符串来使其工作。

    在 Java 中,我创建了一个简单的模型:

    public class PayloadHolder {
      private String payload;
      // getters and setters
    }
    

    然后我更新了我的控制器的方法签名:

    @RequestMapping(value = "/pdf", method = RequestMethod.POST, produces = "application/pdf")
    public ResponseEntity<byte[]> generatePdf(
            @RequestBody(required = true) PayloadHolder payload,
            HttpServletRequest request)
    

    最后改变了AngularJS的请求:

    var pdf; // base-64 encoded string in this variable
    var request = {
        payload : pdf
    };
    var config = {
        headers: { 'Accept': 'application/pdf' },
        responseType : "arrayBuffer"
    };
    
    $http.post("/convert/pdf", request, config)
          .then( function onSuccess(result) { 
             var file = new Blob([result.data], {type: 'application/pdf'});
             window.navigator.msSaveOrOpenBlob(file, "result.pdf");
           },
           function onFailure() { /* log and do failure case stuff */ });
    

    我不确定为什么 POST 不能使用参数工作,特别是因为我在文档中找不到任何表明它不应该工作的内容。我将发布一个后续问题来解决这个问题。

    【讨论】:

    • 如果这对您有用,请接受它作为正确答案,以便其他人可以先尝试,而不是通过其他答案。
    • 这不是解决方案。这是一种解决方法。它也没有回答为什么我的第一次尝试没有奏效,即使它应该(据我所知)。@Bopsi
    【解决方案2】:

    对于 $http.post,如果您没有在服务器端(spring mvc)为请求指定其他内容类型,请将内容类型设置为 url-form-encoded,因为角度可能会与其他内容类型一起发送。

    例子:

    $http({
    method: "post",
    url: URL,
    headers: {'Content-Type': 'application/x-www-form-urlencoded'},
    data: $.param({base64pdf: pdf})
    }).success(function (result){
             var file = new Blob([result.data], {type: 'application/pdf'});
             window.navigator.msSaveOrOpenBlob(file, "result.pdf");
    });
    

    【讨论】:

      【解决方案3】:

      尝试将方法上的@RequestMapping 注释更改为:

      @RequestMapping(value = "/convert", produces = "application/pdf",method = RequestMethod.POST)
      

      或者,尝试以下 AngularJS POST:

      var req = { method: 'POST', url: 'example.com', headers: { 'Content-Type': 'application/pdf' }, data: { base64pdf: pdf } } 
      $http(req).then(function(){...}, function(){...});
      

      【讨论】:

      • 不,在方法注解中添加produces = "application/pdf" 没有任何区别;请求仍然以同样的方式失败。即使在控制器和方法注释上都设置了produces = ,行为仍然相同。
      • var req = {方法:'POST',url:'example.com',标题:{'Content-Type':'application/pdf'},数据:{base64pdf:pdf}} $http(req).then(function(){...}, function(){...});
      • 我已将您的第二条评论复制到您的答案正文中;您应该直接修改您的答案,而不是将答案添加为 cmets。 :)
      • 我是第一次上stackoverflow
      • 欢迎。我现在将尝试您的第二个建议,看看效果如何。
      猜你喜欢
      • 1970-01-01
      • 2018-01-13
      • 1970-01-01
      • 2014-04-17
      • 1970-01-01
      • 2014-09-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多