Capture and upload image to server using Ionic 4

17,046

Solution 1

Here's a detailed way to upload captured images taken by Ionic 4 to backend server.

Let's assume that we have the following class CaptureImage dedicated to capture and upload the taken image

export class CaptureImage  implements OnInit {
 constructor() { }
  ngOnInit() {
  }
}

First, we need to install @ionic-native/camera using :

ionic cordova plugin add cordova-plugin-camera
npm install @ionic-native/camera

you can refer to Ionic Documentation

Then you need to declare a camera object in to your class, so our class will become:

import { Camera, CameraOptions } from '@ionic-native/camera/ngx';

export class CaptureImage implements OnInit {

  //image to be displayed in template
  image;
  imageData;
  constructor(private camera: Camera) { }
          ngOnInit() {
          }
}

Next we need a trigger in our template, in order to call a function which will capture the picture, so the template (capture-image.page.html) will look something like this:

<ion-fab vertical="bottom" horizontal="end" slot="fixed">
      <ion-fab-button (click)="capture()">
          <ion-icon ios="ios-camera" md="md-camera"></ion-icon>
      </ion-fab-button>
 </ion-fab>

Note that we defined a click event which called capture() function. Now we need to implement this function.

So back to our CaptureImage class, here's the capture() function :

  openCamera(){
    const options: CameraOptions = {
    quality: 100,
    destinationType: this.camera.DestinationType.DATA_URL,
    encodingType: this.camera.EncodingType.JPEG,
    mediaType: this.camera.MediaType.PICTURE,
   }

    this.camera.getPicture(options).then((imageData) => {
    this.imageData = imageData;
    this.image=(<any>window).Ionic.WebView.convertFileSrc(imageData);
    }, (err) => {
       // Handle error
       alert("error "+JSON.stringify(err))
  });
}

Please note that the destinationType must be this.camera.DestinationType.DATA_URL

Congratulation ! you have taken the picture , you can view the taken picture in your template using:

<img [src]="image" >     

now we need to upload it to the server. For sake of simplicity, I will implement the upload function directly in the class CaptureImage. but in reality it is better to implement all backend calls in a dedicated service, and then inject it in your class.

So in order to upload the image, we'll need again a trigger, so in our template, let's define an upload button :

 <ion-button (click)="upload()" color="success">
    <ion-icon slot="icon-only" name="checkmark"></ion-icon>
 </ion-button>

So back in the CaptureImage class, let's first inject the HttpClient in the constructor

import { HttpClient } from '@angular/common/http';

export class CaptureImage implements OnInit {
  //image to be displayed in template
  image;
  imageData;
  constructor(private camera: Camera,
          private http: HttpClient){ }
          ngOnInit() {
          }
   }

then,let's define the upload() function :

upload(){
  let  url = 'your REST API url';
  const date = new Date().valueOf();

  // Replace extension according to your media type
  const imageName = date+ '.jpeg';
  // call method that creates a blob from dataUri
  const imageBlob = this.dataURItoBlob(this.imageData);
  const imageFile = new File([imageBlob], imageName, { type: 'image/jpeg' })

  let  postData = new FormData();
  postData.append('file', imageFile);

  let data:Observable<any> = this.http.post(url,postData);
  data.subscribe((result) => {
    console.log(result);
  });
}

Almost done ! we still need to implement one more function : dataURItoBlob, this function creates blob files from dataURLI:

dataURItoBlob(dataURI) {
  const byteString = window.atob(dataURI);
  const arrayBuffer = new ArrayBuffer(byteString.length);
  const int8Array = new Uint8Array(arrayBuffer);
  for (let i = 0; i < byteString.length; i++) {
    int8Array[i] = byteString.charCodeAt(i);
   }
  const blob = new Blob([int8Array], { type: 'image/jpeg' });    
 return blob;
}

Finally, this is how the CaptureImage class will look at the end :

import { Observable } from 'rxjs';
import { OnInit } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Camera, CameraOptions } from '@ionic-native/camera/ngx';

export class CaptureImage implements OnInit {

 //image to be displayed in template
 image;
 imageData;
constructor(private camera: Camera,
        private http: HttpClient) { }
  ngOnInit() {
  }

  openCamera(){
    const options: CameraOptions = {
    quality: 100,
    destinationType: this.camera.DestinationType.DATA_URL,
    encodingType: this.camera.EncodingType.JPEG,
    mediaType: this.camera.MediaType.PICTURE,
   }

    this.camera.getPicture(options).then((imageData) => {
    this.imageData = imageData;
    this.image=(<any>window).Ionic.WebView.convertFileSrc(imageData);
    }, (err) => {
       // Handle error
       alert("error "+JSON.stringify(err))
  });
}
  upload(){
    let  url = 'your REST API url';
    const date = new Date().valueOf();

    // Replace extension according to your media type
    const imageName = date+ '.jpeg';
    // call method that creates a blob from dataUri
    const imageBlob = this.dataURItoBlob(this.imageData);
    const imageFile = new File([imageBlob], imageName, { type: 'image/jpeg' })

    let  postData = new FormData();
    postData.append('file', imageFile);

    let data:Observable<any> = this.http.post(url,postData);
    data.subscribe((result) => {
      console.log(result);
    });
  }

  dataURItoBlob(dataURI) {
    const byteString = window.atob(dataURI);
   const arrayBuffer = new ArrayBuffer(byteString.length);
    const int8Array = new Uint8Array(arrayBuffer);
    for (let i = 0; i < byteString.length; i++) {
      int8Array[i] = byteString.charCodeAt(i);
     }
    const blob = new Blob([int8Array], { type: 'image/jpeg' });    
   return blob;
  }
}

Solution 2

Try converting to Blob Data before assigning to FormData Object.

const formData = new FormData();
const imgBlob = new Blob([reader.result], {
   type: file.type
});
formData.append('file', imgBlob, file.name);

Reference Link: https://devdactic.com/ionic-4-image-upload-storage/

Share:
17,046

Related videos on Youtube

Faouzi
Author by

Faouzi

Updated on June 04, 2022

Comments

  • Faouzi
    Faouzi almost 2 years

    I've been trying to capture an image and upload it to the server for days but no luck. I'm using Ionic 4 for my client side, and Java as my backend (I used Jersey to expose my backend to REST).

    Now, the problem is that after taking the image, and try to upload it I keep recieving a null in my backend.

    Here is my client side code :

        openCam(){
        const options: CameraOptions = {
          quality: 100,
          destinationType: this.camera.DestinationType.FILE_URI,
          encodingType: this.camera.EncodingType.JPEG,
          mediaType: this.camera.MediaType.PICTURE,
          correctOrientation: true,
          cameraDirection: 1
        }
    
        this.camera.getPicture(options).then((imageData) => {
         // imageData is either a base64 encoded string or a file URI
         // If it's base64 (DATA_URL):
         //alert(imageData)
         this.imageData = imageData;
         this.image=(<any>window).Ionic.WebView.convertFileSrc(imageData);
         this.isImageCaptureed = true;
    
        }, (err) => {
         // Handle error
         alert("error "+JSON.stringify(err))
        });
      }
    
    
        upload(){
          let  url = 'http://mydommain/api/upload';
          let dataURL  = 'data:image/jpeg;base64,' + this.imageData;
          let  postData = new FormData();
          postData.append('file', dataURL);
    
          let data:Observable<any> = this.http.post(url,postData);
          data.subscribe((result) => {
            console.log(result);
          });
      }
    

    I tried to pass imageData directly to FormData object,I also tried to convert it using DataURIToBlob() funcion as I found on some other similar problem but still no luck..

        dataURItoBlob(dataURI) {
        // convert base64/URLEncoded data component to raw binary data held in a string
        var byteString;
        if (dataURI.split(',')[0].indexOf('base64') >= 0)
            byteString = atob(dataURI.split(',')[1]);
        else
            byteString = unescape(dataURI.split(',')[1]);
    
        // separate out the mime component
        var mimeString = dataURI.split(',')[0].split(':')[1].split(';')[0];
    
        // write the bytes of the string to a typed array
        var ia = new Uint8Array(byteString.length);
        for (var i = 0; i < byteString.length; i++) {
            ia[i] = byteString.charCodeAt(i);
        }
    
        return new Blob([ia], {type:mimeString});
    }
    

    I know the problem is just in the imageData format. Since I managed to send to select a file using HTML input tag, and upload it using the same upload() function mentioned above and my backend API.

  • alex87
    alex87 almost 4 years
    For anyone that is getting "Failed to execute 'atob' on 'Window'" The imageData returned from getPicture has some junk at the start of it. You need to use imageData.splice() to remove it. On Android the junk is http://localhost/_app_file_. I haven't tested on iOS yet.
  • shivam srivastava
    shivam srivastava about 3 years
    Not working!! File object doesn't accept any parameters: File([imageBlob], imageName, { type: 'image/jpeg' })