简体   繁体   中英

HTTP POST FormData from Angular2 client to Node server

I have an angular2 client and I'm trying to send a file to my NodeJS server via http using a FormData object.

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: {}
});

How can I access the FormData object in server.js and getthe file object that was uploaded? Also, why am I unable to use any FormData functions in upload.component.ts? All the functions throw TypeError: x is not a function .

Your question is really two questions:

  1. How to POST files from an Angular form to a server?
  2. How to handle POSTed files on a (Node.js) server?

I'm no Node.js expert but a quick search turned up a nice tutorial using the Node.js formidable module to handle file uploads from Node.js.

As for the Angular part, the framework itself isn't of much help as far as file uploads so it all comes down to how would we do that in plain JS?

Your code seems fine. Here is a slightly simplified, working version of it (and a 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));
    }
  }
}

Only notable difference with your code is the way I access the files property of the input file element. In your code, writing <input #fileinput> in your template doesn't automagically create a fileinput property in your component, maybe that's the problem.

Notes:

  • I'm using https://httpbin.org/post as the backend to debug the POST request (it returns all the fields it has received in the request).
  • My file field doesn't have the multiple option but I think the code would work just the same if it did.

In terms of handling files posted to the Node.js server, your issue could well be one of Content-Type . If you are using something like body-parser to handle POSTs on the nodejs server, then it will not understand forms with the content type set to be multipart/form-data , which is what FormData deals in.

Note the comment here :

[body-parser] does not handle multipart bodies, due to their complex and typically large nature. For multipart bodies, you may be interested in the following modules: busboy and connect-busboy; multiparty and connect-multiparty; formidable; multer.

So in other words, you have to user a different module to handle the multipart body that FormData sends. I can recommend formidable , in which case you're server code would look something like:

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');
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM