【问题标题】:Multipart request with AngularJSAngularJS 的多部分请求
【发布时间】:2015-05-08 06:58:36
【问题描述】:

我有一个 API 端点,我必须向它发送一个多部分 HTTP 请求,该请求由两部分组成,file(一个文件系统文件)和data(一个 JSON 对象)。

经过一番研究,我发现了如何在 AngularJS 中执行多部分请求:

$http({
   method: 'POST',
   url: url,
   headers: {
      'Content-Type': 'multipart/form-data'
   },
   data: {
      data: model,
      file: file
   },
   transformRequest: customFormDataObject
});

1) customFormDataObject 函数最初具有以下形式:

customFormDataObject formDataObject = function (data, headersGetter) {
   var fd = new FormData();
   angular.forEach(data, function (value, key) {
      fd.append(key, value);
   });

   var headers = headersGetter();
   delete headers['Content-Type'];

   return fd;
};

此实现的结果是请求的各个部分没有设置contentType

2) 在阅读了更多内容 (https://stackoverflow.com/a/24535293/689216) 之后,我尝试为此使用 BlobcustomFormData 对象看起来像这样(有点乱,基本上第一部分是contentTypeapplication/json,第二部分是image/png):

customFormDataObject = function (data, headersGetter) {
    var fd = new FormData();

    var contentType = 'application/json';
    angular.forEach(data, function (value, key) {
         fd.append(key, new Blob([JSON.stringify(value)], {
             type: contentType
         }));
         contentType = 'image/png';
    });

    var headers = headersGetter();
    delete headers['Content-Type'];

    return fd;
 };

第二种方法为请求的每个部分设置正确的contentType,但它没有为这些部分设置任何值。

基本上发生的情况是 1) 在多部分中设置了正确的值,但未设置 contentType。使用 2) 会设置 contentType,但不会设置多部分的值。

我错过了什么吗?这个功能不应该像这样工作吗?

谢谢!

【问题讨论】:

  • 您是否尝试过根据请求设置内容类型而不是每个表单数据元素?
  • 是的,在执行delete headers['Content-Type'];时会自动设置请求的内容类型
  • file 已经是BlobJSON.stringify 不会序列化文件的内容,而是序列化file 的属性(如果有的话)。

标签: angularjs multipartform-data multipart


【解决方案1】:

在 Angular 中上传文件的最简单方法:

var fd = new FormData();
fd.append('file', file);
fd.append('data', 'string');
$http.post(uploadUrl, fd, {
   transformRequest: angular.identity,
   headers: {'Content-Type': undefined}
})
.success(function(){
})
.error(function(){
});

配置对象的以下两个属性是绝对必要的:

transformRequest: angular.identity

覆盖 Angular 的默认序列化,保持我们的数据完整。

headers: {'Content-Type': undefined }

让浏览器检测到正确的Content-Typemultipart/form-data,并填写正确的边界。

没有其他东西对我有用!由 Louthan 夫人的wonderful blogpost 提供。

【讨论】:

  • 很好的答案。简单有效的解决方案。
  • 这就是答案
  • 谢谢!有时,使用 XMLHttpRequest 会有所帮助。
【解决方案2】:

你有没有尝试过这样的事情:

$httpProvider.defaults.transformRequest = function(data) {
  if (data === undefined){
    return data;
  }

  var formData = new FormData();
  for (var key in data){
    if(typeof data[key] == 'object' && !(data[key] instanceof File)){
      formData.append(key, JSON.stringify(data[key]));
    }else{
      formData.append(key, data[key]);
    }
  }
  return formData;
};

【讨论】:

    【解决方案3】:

    我只是解决了完全相同的问题。

    经过一些测试,我遇到了您描述的这种情况:

    基本上发生的情况是 1) 在多部分中设置了正确的值,但未设置 contentType。使用 2) 设置了 contentType,但没有设置多部分的值。

    为了解决这个问题,我不得不使用 Blob 和 Post Ajax 而不是 $http Post

    在这种情况下,$http 似乎无法正常工作。

    代码:

        var formData = new FormData();
    
        var blobId = new Blob([100], { 'type':'text/plain' });
        formData.append('testId', blobId);
    
        var blobImage = fileService.base64ToBlob(contentToDecode, 'image/jpeg');
        formData.append('file', blobImage, 'imagem' + (i + 1) + '.jpg');
    

    请求:

        $.ajax({
          url: url,
          data: formData,
          cache: false,
          contentType: false,
          processData: false,
          type: 'POST',
          success: function(response) {
            deferred.resolve(response);
            $rootScope.requestInProgress = false;
          },
          error: function(error) {
            deferred.reject(error);
            $rootScope.requestInProgress = false;
          }
        });
    

    【讨论】:

    • 请贴一些代码来帮助用户朝着正确的方向前进。
    • 是的,一些代码会对我有很大帮助。我已经完全推迟了这个功能,但我想在某个时候让它滚动。
    【解决方案4】:

    您可以使用https://github.com/danialfarid/ng-file-upload/

    在此文件上传器中,如您在上面的问题中提到的,有一个单独发送文件和数据(JSON 格式)的规定。

    例如:-

    var upload = $upload.upload({
                     url: url,
                     file: file,
                     method: 'POST' or 'PUT', default POST,
                     headers: {'Content-Type': 'multipart/form-data'}, // only for html5
                     fileName: 'doc.jpg',
                     fileFormDataName: 'myFile',
                     data: {'data': model}
                 });
    

    在上面的代码中,您可以使用“multipart/form-data”、文件和数据对象分别作为 JSON 发送 POST 或 PUT 请求。

    更多信息您可以访问上面的链接并查看插件的ReadMe.md。

    我知道我的方法与您目前采用的方法有些不同,但目标是相同的。

    【讨论】:

      【解决方案5】:

      我解决这个问题的方法是。

      var formData = new FormData(document.getElementById('myFormId'));
      

      然后为我服务

                      var deferred = $q.defer();
                      $http.post('myurl', formData, {
                          cache: false,
                          contentType: false,
                          processData: false,
                      })
                          .success(function (response) {
                              deferred.resolve(response);
                          })
                          .error(function (reject) {
                              deferred.reject(reject);
                          });
                      return deferred.promise;
      

      【讨论】:

        猜你喜欢
        • 2017-01-27
        • 2017-09-21
        • 1970-01-01
        • 2018-01-26
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多