Отправка файлов POST запросом в Angular 2

6213
views

FormData in angular 2

Когда возникает такая тривиальнейшая задача как отправка картинки $http запросом, ожидаешь, что ничего сложного возникнуть не может и такой функционал должен бы решаться на уровне фреймворка. Но не все так просто, как хотелось бы. Поэтому хочу поделиться небольшими примерами того, как отправлять файл и как можно отправить его вместе с другими полями формы.

Обработка прикрепления файлов

В первую очередь необходимо получить файлы из input. Просто присвоить модель не выйдет, ведь ngModel не работает с input type file. Они будут храниться в объекте события, который в angular называется $event, его мы и передадим в качестве параметра функции addPhoto().

<input type="file" name="photo" (change)="addPhoto($event)"/>

Сама же функция, которая будет присваивать список прикрепленных файлов в переменную компонента, будет выглядеть так:

addPhoto(event) {
    let target = event.target || event.srcElement;
    this.files = target.files;
}

event.srcElement — используется для поддержки кроссбраузерности.

Теперь для отправки файлов POST запросом необходимо создать FormData и к ней прикрепить полученные ранее файлы.

if (this.files) {
      let files :FileList = this.files;
      const formData = new FormData();
      for(let i = 0; i < files.length; i++){
           formData.append('photo', files[i]);
      }
}

Для отправки нескольких файлов, input должен содержать атрибут multiple.

Данный код можно вставить как в функцию addPhoto(), что позволит автоматически заливать файл на сервер при его выборе, так и в функцию сабмита формы.

Отправка FormData

После генерации формы остается лишь отправить её ангуларовским $http запросом. При этом указывать Content-Type нет необходимости, angular сам  обнаружит и пропишет multipart/form-data и boundary.

this.http.post('endpoint-url', formData).map((response: Response) => response.json()).subscribe(resp => {
     //код выполняемый при успехе
});

Раз уж мы отправляем файл по сабмиту формы, хотелось бы чтобы с файлом отправлялись и остальные поля формы. При этом поля могут быть не просто строками, а содержать вложенные объекты. Задачу достаточно легко решить путем преобразования модели формы в текстовую json строку. И добавить эту строку как поле FormData:

formData.append('data', JSON.stringify(this.form));

Финальный вид всего компонента

Суммируя выше написанный код в единый компонент, получим следующее:

<form name="form" (ngSubmit)="f.form.valid && submitRegister()" #f="ngForm" novalidate>
    <input type="text" placeholder="Фамилия" name="last" [(ngModel)]="form.name.last" #last="ngModel"/>
   <input type="text" placeholder="Имя" name="first" [(ngModel)]="form.name.first" #first="ngModel"/>
   <input type="file" name="photo" (change)="addPhoto($event)"/>
   <button type="submit">Отправить</button>
</form>
import { Component } from '@angular/core';
import { Http } from '@angular/http';

@Component({
    templateUrl: './form.component.html'
})
export class FormComponent {
    form: any = {};
    files: any;
    constructor(public http:Http) {
        this.form = {
            name: {}
        };
    }

    addPhoto(event) {
        let target = event.target || event.srcElement;
        this.files = target.files;
    }

    submitRegister() {
        let final_data;
        if (this.files) {
            let files: FileList = this.files;
            const formData = new FormData();
            for (let i = 0; i < files.length; i++) {
                formData.append('photo', files[i]);
            }

            formData.append('data', JSON.stringify(this.form));
            
            final_data = formData;
        } else {
            //Если нет файла, то слать как обычный JSON
            final_data = this.form;
        }

        this.http.post('/api/form', final_data).map((response: Response) => response.json()).subscribe(resp => {
            //...
        })
    }
}

Итог

Конечно, данный код — лишь пример, и в вашем компоненте запрос будет вынесен в отдельный сервис, а имена переменных будут продуманней. Данный принцип отправки файлов подходит для реализации и в первой версии Angular. Отличия будут лишь в синтаксисе фреймворка.

Если вам полезен данный материал, подписывайтесь на мой блог, чтобы получать уведомления о новых публикациях на темы: верстки, javascript и разработки кроссплатформенных мобильных приложений.

Подписаться на блог по эл. почте