How to upload image/file with formgroup value to A

2019-07-19 19:09发布

问题:

How to upload image and add it to the form when service doing http.post? example product module needs name, price, coverImage. I tried to use many ways that I can get from the internet, forum, etc. But still got no clue about this issue.

I am using model driven form and append it when image is selected and i tried to print out the values of form before send to API. attached image for console.log result:

fyi: I am using API based on PHP Laravel. I tried to get the image from "$request->file('image');"

anybody can help me for this case? thanks.

回答1:

Part 1. Upload files to API via FormData

In your service file like upload.service.ts you need to create function to upload files via POST method. Here is an example for this function:

getUploadHeaders() {
    let headers = new Headers({'Accept': 'application/json'});
    headers.delete('Content-Type');
    return headers;
}    

addNewPost(newPost: FormData): Observable<FormData> {
    const endpoint = 'https://yourApiUrl.com'; 
    return this.http
        .post(endpoint, newPost, { headers: this.getUploadHeaders() })
        .catch((e) => this.handleError(e));
}

And now you should create upload function in your component, for example upload.component.ts. Here is an example for upload function with FormData.

fileToUpload: File = null;

constructor(private uploadService: UploadService) { }

handleFileInput(files: FileList) {
  this.fileToUpload = files.item(0);
}


saveFileToApi() {
  const saveForm: FormData = new FormData();      
  if (this.fileToUpload) {
    // if you need/want to append other fields to API endpoint
    saveForm.append('name', this.name);
    saveForm.append('link', this.link);
    saveForm.append('description', this.description);
    // if you have to append number
    saveForm.append('age', this.age.toString());
    // append image
    saveForm.append('fileFieldNameOnYourApi', this.fileToUpload, this.fileToUpload.name);
  }

  this.uploadService.addNewPost(saveForm).subscribe(() => {
    console.log('Upload success!');
  }, error => {
    console.log('Upload failed!');
  });
}

In saveFileToApi-function you can also append other fields of your form. Beware that you should convert number fields to string. Here you can read more about usage if FormData Objects.

For uploading an file via FormData you need 3 parameters: propertyName on API endpoint, file and name of this file. And in your upload.component.html you need to have form like this one:

<form (ngSubmit)="onSubmit()">     
    <label for="fileField">Chose file to upload</label>
    <input type="file"
           id="fileField"
           name="fileField"
           (change)="handleFileInput($event.target.files)">
    <button type="submit">Speichern</button>
</form>

For more detail of FormData read this and for more information about FormData.append() read this.

Part 2. Get uploaded image from API

You should set responseType: ResponseContentType.Blob in your GET-Request settings, because so you can get your image as blob and convert it later da base64-encoded source. You code above is not good. If you would like to do this correctly, then create separate service to get images from API. Beacuse it ism't good to call HTTP-Request in components.

Here is an working example:

Create image.service.ts or use upload.service.ts from part 1 (maybe you can give that service another name) and put following code:

    getImage(imageUrl: string): Observable<File> {
        return this.http
            .get(imageUrl, { responseType: ResponseContentType.Blob })
            .map((res: Response) => res.blob());
    }

Now you need to create some function in your image.component.ts to get image and show it in html.

For creating an image from Blob you need to use JavaScript's FileReader. Here is function which creates new FileReader and listen to FileReader's load-Event. As result this function returns base64-encoded image, which you can use in img src-attribute:

imageToShow: any;

createImageFromBlob(image: Blob) {
       let reader = new FileReader();
       reader.addEventListener("load", () => {
          this.imageToShow = reader.result;
       }, false);

       if (image) {
          reader.readAsDataURL(image);
       }
}

Now you should use your created ImageService to get image from api. You should to subscribe to data and give this data to createImageFromBlob-function. Here is an example function:

getImageFromService() {
      this.isImageLoading = true;
      this.imageService.getImage(yourImageUrl).subscribe(data => {
        this.createImageFromBlob(data);
        this.isImageLoading = false;
      }, error => {
        this.isImageLoading = false;
        console.log(error);
      });
}

Now you can use your imageToShow-variable in HTML template like this:

<img [src]="imageToShow"
     alt="Place image title"
     *ngIf="!isImageLoading; else noImageFound">
<ng-template #noImageFound>
     <img src="fallbackImage.png" alt="Fallbackimage">
</ng-template>

I hope this description is clear to understand and you can use it in your project.