How to upload image from library or camera with CRNA

CRNA (create react native app ) works with expo, so I will demo an example help you access the library from your device or access a camera, take a photo and upload to a server. In this post, I only demo front end with react native. To display the system UI for choosing an image or a video from the phone’s library you need use ImagePicker In Screen component, prepare a component like this

import React, { Component } from 'react'
import { ImagePicker, FileSystem } from 'expo'
import { View,  Button } from 'react-native'

export class HomeScreen extends Component
    constructor(props) {
        super(props)
        this.state = {
            imageUrl: 'https://sigma-static-files.imgix.net/default_profile_pic.png'
        }
    }
    render() {
        return() {
            <View>
                <Image source={{uri: this.state.imageUrl}} />
                <Button 
                    onPress={this.takePhoto}
                    title={'Take a photo'}
                />
               <Button 
                    onPress={this.choosePhoto}
                    title={'Choose a photo'}
                />
            </View>
        }
    }

After that, do funtions to open library or open camera (note that simulator doesn't support test open camera, you need to test this on a real device) This is a function open camera

    takePhoto = async () => {
        let pickerResult = await ImagePicker.launchCameraAsync({
          exif: true,
          allowsEditing: true,
          quality: 0.7,
          base64: true,
          aspect: [4, 3]
        })
    }

This is a function open library

    choosePhoto = async () => {
        let pickerResult = await ImagePicker.launchImageLibraryAsync({
          exif: true,
          allowsEditing: false,
          quality: 0.7,
          base64: true
        })
     }

It returns infomation of an image with:

  • cancelled: false => it means you chosed an image (not cancel when open library)
  • uri: file:///.... => absolute path of image
  • witdh: number
  • height: number
  • base64: //string too long => this string contains the image jpeg data Handle upload an image to server, depends on type of data that backend require, you need upload a right type. This is an example upload file with binary type
   handleUploadPhoto = pickerResult => {
        const userId = this.props.account.rawId,
              { uploadAvatarProcessing } = this.props,
                  cropInfo = {
                        x: pickerResult.width / 2 - 301, // get center image
                        y: pickerResult.height / 2 - 301,
                        width: 602,
                        height: 602
                  },
                  contentType = 'image/jpeg',
                  fileName = pickerResult.uri.split('/').pop()
                  FileSystem.getInfoAsync(pickerResult.uri).then(info => {
                  uploadAvatarProcessing(
                        userId,
                        fileName,
                        contentType,
                        info.size,
                        cropInfo,
                        pickerResult
              )
        })
  }

I use sagas and apisauce to help upload image. Finish, you call handleUploadPhoto at each function get image from your device.

  takePhoto = async () => {
        let pickerResult = await ImagePicker.launchCameraAsync({
          exif: true,
          allowsEditing: true,
          quality: 0.7,
          base64: true,
          aspect: [4, 3]
        })
        if (!pickerResult.cancelled) {
          this.handleUploadPhoto(pickerResult)
        }
    }
    
    choosePhoto = async () => {
        let pickerResult = await ImagePicker.launchImageLibraryAsync({
          exif: true,
          allowsEditing: false,
          quality: 0.7,
          base64: true
        })
        if (!pickerResult.cancelled) {
          this.handleUploadPhoto(pickerResult)
        }
     }
     

Extension, this is an example converst file from base64 to binary

   function convertToByteArray(input) {
  var binary_string = atob(input);
  var len = binary_string.length;
  var bytes = new Uint8Array(len);
  for (var i = 0; i < len; i++) {
    bytes[i] = binary_string.charCodeAt(i);
  }
  return bytes
}

function atob(input) {
  const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';

  let str = input.replace(/=+$/, '');
  let output = '';

  if (str.length % 4 === 1) {
    throw new Error("'atob' failed: The string to be decoded is not correctly encoded.");
  }
  let bc = 0, bs = 0, buffer, i = 0

  for (let bc = 0, bs = 0, buffer, i = 0;
    buffer = str.charAt(i++);

    ~buffer && (bs = bc % 4 ? bs * 64 + buffer : buffer,
      bc++ % 4) ? output += String.fromCharCode(255 & bs >> (-2 * bc & 6)) : 0
  ) {
    buffer = chars.indexOf(buffer);
  }

  return output;
}

If server requires type of file is binary, you can use this to convert image file and upload ( this code use apisauce and sagas)

const responsePhoto = yield call(() =>
        API.post(
          url,
          convertToByteArray(action.pickerResult.base64),
 ))

After uploading, you should save image url into reducer and update on UI. If you don't know how to work with sagas or apisauce, you can leave message, I can help you work with them


All Rights Reserved