I also got this problem, but we can deal with file
and other form field separately.
This is my code, I want to post a FormData Object
to Django REST API, this formData
contains both strings and image, also with user_name
and password
. I created a component - upload-image-form
This is what I want to post, an Image
class(I define it by myself)
// images.ts
export class Image {
constructor(
public id: number,
public created: string, // set by back end
public userId: number,
public fileUrl: string,
public owner: string,
public des?: string,
public localImage?: File,
) { }
}
This is my component
// upload-image-form.component.ts
import { Component, OnInit, Input } from '@angular/core';
import { NgForm } from '@angular/forms/forms';
import { FormControl, FormGroup, FormBuilder } from '@angular/forms';
import { Image } from '../image';
import { User } from '../user';
import { ImageService } from '../image.service';
@Component({
selector: 'app-upload-image-form',
templateUrl: './upload-image-form.component.html',
styleUrls: ['./upload-image-form.component.css']
})
export class UploadImageFormComponent implements OnInit {
@Input() user: User; // this value comes from parent component user-detail's tempolate
private file: File;
private formData: FormData = new FormData();
private imageForm: FormGroup;
private submitted = false;
private imageUrl = 'http://192.168.201.211:8024/images/';
private password: string;
constructor(
private imageService: ImageService,
private fb: FormBuilder,
) { }
ngOnInit() {
console.log(this.user);
this.createFrom(this.user); // create form here, so we can get this.user's value
}
createFrom(user: User) {
// I didn't put file field in this form
this.imageForm = this.fb.group({
id: 1,
created: '20170825',
userId: user.id,
fileUrl: 'http://images.fineartamerica.com/images-medium-large-5/mt-shuksan-picture-lake-dennis-romano.jpg',
owner: user.username,
des: '',
pw: '',
})
console.log(user);
}
// https://stackoverflow.com/a/41465502/2803344
// get a file object from form input
onChange(event: EventTarget) {
let eventObj: MSInputMethodContext = <MSInputMethodContext> event;
let target: HTMLInputElement = <HTMLInputElement> eventObj.target;
let files: FileList = target.files;
this.file = files[0];
}
onSubmit() {
// deal with string fields and file separately
this.submitted = true;
console.log(this.file); // file is here, captured by onChange()
console.log(this.imageForm.value); // other fields are here, captured by formGroup
this.formData.append('localImage', this.file, this.file.name);
for (let item in this.imageForm.value) {
console.log(item)
if (item !== 'pw') {
this.formData.append(item, this.imageForm.value[item]);
}
else {
this.password = this.imageForm.value[item];
}
}
// console.log('###here is the total form data');
console.log(this.formData);
console.log(this.formData.get('fileUrl'));
console.log(this.user.username);
this.imageService.post(this.formData, this.user.username, this.password)
.then(res =>{
console.log(res);
});
}
onClick(form: FormGroup) {
form.reset({
userId: this.user.id,
owner: this.user.username,
created: '20170825',
fileUrl: 'http://www.fujifilm.com.sg/Products/digital_cameras/x/fujifilm_x_t1/sample_images/img/index/ff_x_t1_001.JPG',
})
this.submitted=false;
console.log(form.value);
}
}
This is my template
<!-- upload-image-form.component.html -->
<div [hidden]="submitted" *ngIf="user">
<h2>Upload New Image</h2>
<form [formGroup]="imageForm" (ngSubmit)="onSubmit()" >
<div class="form-group">
<label for="fileUrl">File Url</label>
<input type="url" class="form-control" id="fileUrl"
formControlName="fileUrl" required>
<div [hidden]="imageForm.get('fileUrl').valid || imageForm.get('fileUrl').pristine"
class="alert alert-danger">
File Url is required
</div>
<p>{{imageForm.get('fileUrl').valid | json}}</p>
<p>{{imageForm.get('fileUrl').value | json}}</p>
</div>
<!-- upload an image
don't define this field in formGroup-->
<div class="form-group">
<label for="localImage">Local File</label>
<input type="file" class="form-control" id="localImage"
(change)="onChange($event)" accept=".jpg, .png" >
</div>
<div class="form-group">
<label for="des">Description</label>
<input type="text" class="form-control" id="des"
formControlName="des">
</div>
<div class="form-group">
<label for="userId">User ID</label>
<input type="text" class="form-control" id="userId"
formControlName="userId" readonly>
<p>{{imageForm.get('userId').value | json}}</p>
</div>
<div class="form-group">
<label for="owner">Owner</label>
<input type="text" class="form-control" id="owner"
formControlName="owner" readonly>
</div>
<!-- input user's password -->
<div class="form-group">
<label for="pw">password</label>
<input type="password" class="form-control" id="pw"
formControlName="pw" required>
</div>
<button type="submit" class="btn btn-success" [disabled]="!imageForm.valid">Submit</button>
</form>
</div>
At last, this is my image service
// image.service.ts
import { Injectable } from '@angular/core';
import { Headers, Http, RequestOptions } from "@angular/http";
import 'rxjs/add/operator/toPromise';
import { Image } from "./image";
import { User } from './user';
@Injectable()
export class ImageService {
private imageUrl = 'http://192.168.201.211:8024/images/';
//set headers for authorization, https://stackoverflow.com/a/34465070/2803344
createAuthorizationHeader(headers: Headers, name: string, pw: string) {
headers.append('Authorization', 'Basic ' +
btoa(`${name}:${pw}`));
}
createOptions(name: string, pw: string) {
let headers = new Headers();
this.createAuthorizationHeader(headers, name, pw);
// headers.append('Content-Type', 'application/json'); // without this
// headers.append('Content-Type', 'multipart/form-data'); // without this
let options = new RequestOptions({ headers: headers });
return options;
}
constructor(private http: Http) { }
getImageById(id: number): Promise<Image> {
const url = `${this.imageUrl}`;
console.log(url);
let headers = new Headers();
let token = 'token';
headers.append('X-Auth-Token', token);
return this.http.get(url, {headers: headers})
.toPromise()
// .then(res => res.json().data as Image)
.then(res => console.log(res))
.catch(this.handleError);
}
getImageByUrl(url: string): Promise<Image> {
return this.http.get(url)
.toPromise()
.then(res => res.json() as Image)
// .then(res => console.log(res))
.catch(this.handleError);
}
post(formData: FormData, user: string, pw: string): Promise<Image> {
let options = this.createOptions(user, pw);
console.log('we will have a post!');
console.log(formData.get('localImage'));
console.log(formData.get('fileUrl'));
console.log(formData.get('des'));
return this.http.post(this.imageUrl, formData, options)
.toPromise()
.then(res => res.json() as Image)
.catch(this.handleError);
}
private handleError(error: any): Promise<any> {
console.error('An error occurred', error); // for demo purposes only
return Promise.reject(error.message || error);
}
}
You can find the whole project in github, https://github.com/OnlyBelter/image-sharing-system2.