用于PDF数据的AngularJS POST总是失败

时间:2015-12-08 00:58:58

标签: java angularjs spring-web

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

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

对于Internet Explorer,我正在尝试将POST发送回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服务。转换控制器如下所示:

@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的开发人员工具网络流量捕获,我可以观察正在进行的请求。该请求包含params中完整且正确的base-64编码字符串,该字符串存在于请求正文中。

  • 请求标头如下所示:

Request         POST /convert/pdf HTTP/1.1
Content-Type    application/json;charset=utf-8
Accept          application/json, text/plain, */*
Referer         localhost:8080/
Accept-Language en-US
Accept-Encoding gzip, deflate
User-Agent      Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) like Gecko
Host            localhost:8080
Content-Length  5699
DNT             1
Connection      Keep-Alive
Cache-Cont

rol   no-cache
  • “响应”标题显示:

Response                     HTTP/1.1 400 Bad Request
Server                       Apache-Coyote/1.1
Access-Control-Allow-Origin  *
Access-Control-Allow-Methods POST,GET,OPTIONS,DELETE
Access-Control-Max-Age       3600
Access-Control-Allow-Headers x-requested-with
Content-Type                 application/pdf
Content-Length               98
Date                         Mon, 07 Dec 2015 21:54:32 GMT
Connection                   close
  • 响应正文包含无效的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的建议通过via data: { base64pdf : pdf }传递参数。

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

  3. 我正在使用:

    • AngularJS 1.4.8
    • Spring 4.2.3.RELEASE
    • Java 8
    • Tomcat 8

3 个答案:

答案 0 :(得分:0)

我最终通过在请求正文中发送base64pdf字符串来实现这一点。

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

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

然后我更新了我的Controller的方法签名:

@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不能使用params,特别是因为我在文档中找不到任何表明它不应该的东西。我将发布一个跟进问题来解决这个问题。

答案 1 :(得分:0)

对于$ http.post,如果您未在服务器端(spring mvc)指定其他内容类型的请求,则将content-type设置为url-form-encoded,因为angular可能与其他内容类型一起发送。< / p>

示例:

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

答案 2 :(得分:-1)

尝试将方法的@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(){...});