In this tutorial, i will show you how to take an image using Capacitor Camera plugin and upload it to our Django backend from our ionic application.
You will find the repository source code here.
Take or choose an image using Capacitor Camera plugin
Let’s imagine we would allow our user to set or change his profile picture. First we will add a new page
ionic g page UploadPicture
Then as always, i will move the new created page in the pages folder and modiy the app-routing.module.ts to reflect this change
{ path: 'upload-picture', loadChildren: './pages/upload-picture/upload-picture.module#UploadPicturePageModule' }
Ok now we can edit the upload-picture.page.ts file to include the capacitor Camera plugin which will be used to take or select a photo
import { Component, OnInit } from '@angular/core'; import { Plugins, CameraResultType,CameraSource } from '@capacitor/core'; const { Camera } = Plugins; @Component({ selector: 'app-upload-picture', templateUrl: './upload-picture.page.html', styleUrls: ['./upload-picture.page.scss'], }) export class UploadPicturePage implements OnInit { constructor() { } ngOnInit() { } }
And then implement a method which will ask the user if he wants to choose or take a photo (default behaviour of the capacitor Camera plugin)
async chooseOrTakePicture() { const image = await Plugins.Camera.getPhoto({ quality: 90, allowEditing: false, resultType: CameraResultType.Base64, //source: CameraSource.Camera }).catch((error)=>{ console.log(error) }) // variable image should contain our base64 image }
You can find all the options available for the Camerag plugin on the Capacitor documentation page
Convert a base64 image into a blob in our Ionic application
The capacitor Camera plugin should return the image in base64 format because we ask for it using the line
resultType: CameraResultType.Base64,
It is better to transform the image into a binary file because sometimes some weird conversion errors can occured between javascript base64 and python base64 format.
To transform a base64 image to a blob we can use this code
public b64toBlob(b64Data, contentType = '', sliceSize = 512) { const byteCharacters = atob(b64Data); const byteArrays = []; for (let offset = 0; offset < byteCharacters.length; offset += sliceSize) { const slice = byteCharacters.slice(offset, offset + sliceSize); const byteNumbers = new Array(slice.length); for (let i = 0; i < slice.length; i++) { byteNumbers[i] = slice.charCodeAt(i); } const byteArray = new Uint8Array(byteNumbers); byteArrays.push(byteArray); } const blob = new Blob(byteArrays, { type: contentType }); return blob; }
So now our method will look like
async chooseOrTakePicture() { const image = await Plugins.Camera.getPhoto({ quality: 90, allowEditing: false, resultType: CameraResultType.Base64, //source: CameraSource.Camera }).catch((error)=>{ console.log(error) }) // variable image should contain our base64 image if (image){ // convert base64 image to blob let blob = this.b64toBlob(image.base64String) } }
Upload image by using a form from our Ionic application to Django
Now that we have our binary image we will use a standard angular form to send data to our Django backend. We will first need to check if network is available and if so, we can send the form
if (this.apiService.networkConnected){
//Create a form to send the file
const formData = new FormData();
//Generate a fake filename
let name =Math.random().toString(36).substring(2, 15) + Math.random().toString(36).substring(2, 10);
formData.append('file', blob, name+.${image.format}
);
formData.append('name', name);
this.apiService.uploadPhoto(formData).subscribe((value)=>{
//server return value
})
}
else{
this.apiService.showNoNetwork()
}
The form will be composed of the file and a fake name that we randomly generate.
We need to create the uploadPhoto method into our usual django-api service and will use the fetch method to send the form. We also need to define our new api endpoint which will be called uploadphotobinary
getUploadPhotoUrlBinary= this.virtualHostName + this.apiPrefix + "/uploadphotobinary/"
uploadPhoto(formData) { var myHeaders = new Headers(); myHeaders.append("Authorization", "Bearer "+ this.tokenSSO); var requestOptions = { method: 'POST', headers: myHeaders, body: formData }; return Observable.create(observer => { fetch(this.getUploadPhotoUrlBinary, requestOptions) .then(response => response.json()) .then(result => { observer.next(result) }) .catch(error => {console.log('error', error); observer.next()}); }) }
Receive and save an image using Django Rest Framework
To receive and save the image in our Django backend, we first need to declare the model that will be used to save photo. So we can add the following models to our models.py file
class Photo(models.Model): id = models.UUIDField(primary_key=True, default=uuid.uuid4) file = models.ImageField(upload_to='dossiers/(%Y_%m)/', null=True, blank=True) createdAt = models.DateTimeField(auto_now_add=True) updatedAt = models.DateTimeField(auto_now=True)
Now we declare our new endpoint in the urls.py file
url(r'^uploadphotobinary/$', PhotoUploadView.as_view()),
Then we declare the PhotoUploadView in our views.py file
//Adding some imports from rest_framework.views import APIView from rest_framework.parsers import FileUploadParser from rest_framework.response import Response from rest_framework import status class PhotoUploadView(APIView): parser_class = (FileUploadParser,) def post(self, request, *args, **kwargs): print("=== DANS POST METHOD") print(request.data) file_serializer = PhotoUploadSerializer(data=request.data) if file_serializer.is_valid(): file_serializer.save() return Response(file_serializer.data, status=status.HTTP_201_CREATED) else: print(file_serializer.errors) return Response(file_serializer.errors, status=status.HTTP_400_BAD_REQUEST)
and we declare the new PhotoUploadSerializer in our serializers.py file
class PhotoUploadSerializer(ModelSerializer): class Meta: model = Photo fields = '__all__'