从Angular2客户端到节点服务器的HTTP POST FormData

时间:2017-01-26 19:46:45

标签: node.js angular form-data

我有一个angular2客户端,我尝试使用FormData对象通过http将文件发送到我的NodeJS服务器。

upload.html

...
<input #fileinput type="file" [attr.multiple]="multiple ? true : null" (change)="uploadFile()" >
...

upload.component.ts

uploadFile() {
    let files = this.fileinput.nativeElement.files;

    if (files.length == 0) return;

    this.formData = new FormData();

    for (var i = 0; i < files.length; i++) {
        console.log("Appended file: " + files[i].name + " File object:" + files[i]); // Output: Appended file: simple.txt File object:[object File]
        this.formData.append(files[i].name, files[i]);
    }
    console.log("uploadFile formData: " + this.formData); // Output: uploadFile formData: [object FormData]
    // this.formData.keys() (or any other function) results in TypeError: dataForm.keys is not a function

    this.userService
        .formDataUpload(this.formData)
        .then((response: any) => {
            console.log(response);
            this.router.navigateByUrl('/');
        });
}

user.service.ts

formDataUpload(formData: FormData): Promise<any> {
    return this.http.post('/api/v0/formdataupload', formData)
    .toPromise()
    .then((response: any) => {
        return response.json()
    })
    .catch(this.handleError);
}

server.js

app.post('/api/v0/formdataupload', function(req, res) {
    console.log(req.body); // Output: {}
});

如何访问server.js中的FormData对象并获​​取上传的文件对象?另外,为什么我无法在upload.component.ts中使用任何FormData函数?所有函数抛出 TypeError:x不是函数

2 个答案:

答案 0 :(得分:2)

你的问题实际上是两个问题:

  1. 如何将文件从Angular表单发布到服务器?
  2. 如何处理(Node.js)服务器上的POST文件?
  3. 我没有Node.js专家,但使用Node.js nice tutorial模块快速搜索了formidable来处理来自Node.js的文件上传。

    至于Angular部分,就文件上传而言,框架本身并没有多大帮助,所以这一切都归结为我们如何在普通JS中做到这一点?

    您的代码似乎很好。这是一个稍微简化的工作版本(和Plunkr):

    @Component({
      selector: 'my-app',
      template: `
        <form [formGroup]="myForm">
          <p><input type="file" formControlName="file1" (change)="uploadFile($event)"></p>
        </form>
      `
    })
    export class AppComponent {
      myForm: ControlGroup;
    
      constructor(fb: FormBuilder, private http: Http) {
        this.myForm = fb.group({
          file1: []
        });
      }
    
      uploadFile(evt) {
        const files = evt.target.files;
        if (files.length > 0) {
          let file;
          let formData = new FormData();
          for (let i = 0; i < files.length; i++) {
            file = files[i];
            formData.append('userfile', file, file.name);
          }
          this.http.post('https://httpbin.org/post', formData)
            .map(resp => resp.json())
            .subscribe(data => console.log('response', data));
        }
      }
    }
    

    与代码的显着区别在于我访问输入文件元素的files属性的方式。在您的代码中,在模板中编写<input #fileinput>并不会自动在您的组件中创建fileinput属性,这可能就是问题所在。

    注意:

    • 我使用https://httpbin.org/post作为后端来调试POST请求(它返回它在请求中收到的所有字段)。
    • 我的文件字段没有multiple选项,但我认为代码可以正常工作。

答案 1 :(得分:0)

在处理发布到Node.js服务器的文件方面,您的问题可能是Content-Type之一。如果您使用body-parser之类的东西来处理nodejs服务器上的POST,那么它将无法理解内容类型设置为multipart/form-data的表单,这是FormData处理的内容。

请注意评论here

  

[body-parser]由于其复杂且通常较大的性质而无法处理多部分主体。对于多部分机构,您可能对以下模块感兴趣:busboy和connect-busboy;多方和连接多方;强大; multer。

换句话说,您必须使用不同的模块来处理FormData发送的多部分主体。我可以推荐formidable,在这种情况下,您的服务器代码应该类似于:

const formidable = require('formidable')

exports.createPost = (req, res, next) => {
    var form = new formidable.IncomingForm();
    form.parse(req, (err, fields, files) => {
        console.log(fields)
        res.send('NOT IMPLEMENTED: pollsController createPost');
    }
}