使用$http在Angular中发送multipart/form-data类型的文件

55

我知道有很多关于这个问题的问答,但我无法让它起作用:

我想将一个multipart/form-data格式的文件从输入框上传到服务器上

我尝试了两种方法。首先是:

headers: {
  'Content-Type': undefined
},

这导致例如对于一张图片

Content-Type:image/png

虽然应该是multipart/form-data

另一个是:

headers: {
  'Content-Type': multipart/form-data
},

但是这需要一个边界头,我认为不应该手动插入...

有什么干净的方法来解决这个问题吗?我已经阅读到可以这样做

$httpProvider.defaults.headers.post['Content-Type'] = 'multipart/form-data; charset=utf-8';

但是我不希望所有我的帖子都是multipart/form-data格式。默认应该是JSON格式。

6个回答

87

请看FormData对象:https://developer.mozilla.org/zh-CN/docs/Web/API/FormData

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

刚刚确认,这个项目不再支持IE 9,所以对我来说非常完美!谢谢! - Thomas Stubbe
15
这个方法可以生效,但为什么要把 'Content-Type' 设置为 'undefined' 呢? - meucaa
7
这段代码片段看起来与这篇博客文章上的内容相似,该文章指出:“通过设置 'Content-Type': undefined,浏览器会为我们设置 Content-Type 为 multipart/form-data,并填充正确的边界。手动设置 'Content-Type': multipart/form-data 将无法填充请求的边界参数。”- https://www.uncorkedstudios.com/blog/multipartformdata-file-upload-with-angularjs/ - gavxn
1
如果我有base64(blob)而不是文件,我如何上传?@jstuartmilne - bhaRATh
1
我正在使用ASP.NET Web API后端进行工作,这正是所需的!然而,我不需要transformRequest部分。谢谢! - Steve Danner

38

这是关于 Angular 4 和 5 的更新答案。TransformRequest和angular.identity已被取消。我还包括了在一个请求中结合文件和JSON数据的能力。

Angular 5 解决方案:

import {HttpClient} from '@angular/common/http';

uploadFileToUrl(files, restObj, uploadUrl): Promise<any> {
  // Note that setting a content-type header
  // for mutlipart forms breaks some built in
  // request parsers like multer in express.
  const options = {} as any; // Set any options you like
  const formData = new FormData();

  // Append files to the virtual form.
  for (const file of files) {
    formData.append(file.name, file)
  }

  // Optional, append other kev:val rest data to the form.
  Object.keys(restObj).forEach(key => {
    formData.append(key, restObj[key]);
  });

  // Send it.
  return this.httpClient.post(uploadUrl, formData, options)
    .toPromise()
    .catch((e) => {
      // handle me
    });
}

Angular 4 解决方案:

// Note that these imports below are deprecated in Angular 5
import {Http, RequestOptions} from '@angular/http';

uploadFileToUrl(files, restObj, uploadUrl): Promise<any> {
  // Note that setting a content-type header
  // for mutlipart forms breaks some built in
  // request parsers like multer in express.
  const options = new RequestOptions();
  const formData = new FormData();

  // Append files to the virtual form.
  for (const file of files) {
    formData.append(file.name, file)
  }

  // Optional, append other kev:val rest data to the form.
  Object.keys(restObj).forEach(key => {
    formData.append(key, restObj[key]);
  });

  // Send it.
  return this.http.post(uploadUrl, formData, options)
    .toPromise()
    .catch((e) => {
      // handle me
    });
}

10
那么问题就在于IE 11。 - Josh Hibschman

5
在Angular 6中,您可以执行以下操作:
在您的服务文件中:
 function_name(data) {
    const url = `the_URL`;
    let input = new FormData();
    input.append('url', data);   // "url" as the key and "data" as value
    return this.http.post(url, input).pipe(map((resp: any) => resp));
  }

在component.ts文件中: 在任何函数(比如说xyz)中,
xyz(){
this.Your_service_alias.function_name(data).subscribe(d => {   // "data" can be your file or image in base64 or other encoding
      console.log(d);
    });
}

4
在Angular 13(~13.0.0)中,不需要指定请求的Content-Type。以下是some.service.ts的示例代码:
public sendFiles(data: any): Observable<any> {
    const formData = new FormData();

    data.files.forEach((file: File) => {
      formData.append('files', file);
    });
    
    const url: string = <YOUR_API_URL>;

    return this.http.post<any>(url, formData);
}
  • Content-Type字段设置为以下值将导致错误:
    • 'file'
    • 未定义(在严格模式下无法使用)
    • 'multipart/form-data'

2

在 Angular 9 中,我曾尝试使用 'Content-Type': undefined,但对我无效。后来,我尝试了下面的代码,对于文件对象它非常有效。

const request = this.http.post(url, data, {
      headers: {
        'Content-Type': 'file'
      },
    });

1
请勿设置、重置或更新内置FormData的Content-Type。这个问题会得到解决。在Angular 7中,它可以正常工作。
虽然存在Auth-Interceptor,但我没有让它设置、更改或重置我的post请求的Content-Type头。让它保持原样,问题就解决了。
这是我的身份验证拦截器图像(链接),我绕过了我的请求。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接