Read Binary file as Base64

I have been trying many ways to read a binary file and convert it to base64 in order to send jpg images from the iPhone and Androids to an internal web-service.

For like a year I was using this method:

const BASE_64_REGEX = /data\:image\/(?:.*);base64,(.*)/

export const convertBlobToBase64 = blob => new Promise((resolve, reject) => {
    const reader = new FileReader
    reader.onerror = reject
    reader.onload = () => {
        try {
            const match = BASE_64_REGEX.exec(reader.result)
            resolve(match[1])
        }
        catch (err) {
            reject(err)
        }
    };
    reader.readAsDataURL(blob)
})

const response = await fetch('path to image file')
const blob = await response.blob()
const data = await convertBlobToBase64(blob)

However, after upgrading to Expo 31 this broke with different errors on Android and iOS. Androids was a Network Error. iOS “works” but no data is returned. I am trying now to make it work with Expo 32.

I have tried the solution priezz suggested. This works on Android, but I get an error on iOS fileToBase64() "File '<filepath>' could not be read."

When will we have an Expo Blessed way to read arbitrary files off of the FileSystem.

After more testing. The base64 in the android apps are producing seem to be nonsense.

Ensure, that you pass the correct URI to the fileToBase64Helper/fileToBase64 function. On iOS you have to pass unescape(uri) while od Android just uri you have got from Expo file routine.

Ok, I added a function called uriCleaner.

now my code looks like this

import base64 from 'base64-js'
import { FileSystem } from 'expo'
import { Platform } from 'react-native'

function stringToUint8Array(str) {
    const length = str.length
    const array = new Uint8Array(new ArrayBuffer(length))
    for (let i = 0; i < length; i++) array[i] = str.charCodeAt(i)
    return array
}

export async function fileToBase64(uri) {
    try {
        const content = await FileSystem.readAsStringAsync(uri)
        return base64.fromByteArray(stringToUint8Array(content))
    } catch (e) {
        console.warn('fileToBase64()', e.message)
        return ''
    }
}

export function uriCleaner(uri) {
    return Platform.OS === 'ios' ? unescape(uri) : uri
}

const signature = await fileToBase64(uriCleaner(item.signature))

Signature is a jpg image on the file system.

The error now is

fileToBase64(), File 'file:///var/mobile/Containers/Data/Application/60DB7C82-31AE-406F-BEFD-88D7C5FFC08A/Documents/ExponentExperienceData/%40kbloom-autologue%2Fautologue-edelivery-app/signatures/7b30c60c5fe218e8de1c7ef68f69b6e9.jpg' isn't readable.

if I remove the unescape(uri) I get

fileToBase64(), File 'file:///var/mobile/Containers/Data/Application/60DB7C82-31AE-406F-BEFD-88D7C5FFC08A/Documents/ExponentExperienceData/%2540kbloom-autologue%252Fautologue-edelivery-app/signatures/d541d7cd7bc704d67c4087c8c8960492.jpg' could not be read.

finally if I apply it twice unescape(unescape(uri))

fileToBase64(), File 'file:///var/mobile/Containers/Data/Application/60DB7C82-31AE-406F-BEFD-88D7C5FFC08A/Documents/ExponentExperienceData/@kbloom-autologue/autologue-edelivery-app/signatures/836bf916d93f43e9e4170252e74a4587.jpg' isn't readable.

The jpg image is created using ExpoPixi.Sketch module.

const { uri } = await this.sketch.takeSnapshotAsync()
const { md5 } = await FileSystem.getInfoAsync(uri, { md5: true })
const signatureFile = `${FileSystem.documentDirectory}signatures/${md5}.jpg`
try {
    await FileSystem.copyAsync({ from: uri, to: signatureFile })
} catch (err) {
    console.error(err)
}

Ok, I solved the problem. Turns out all of this can be replaced with the new options for readAsStringAsync

The issue is the documentation has a minor typo (that started my whole confusion). The three parameters encoding, length, and position are part of the options property not separate positional parameters. so all of that Base64 converting code can be replaced with

const signature = await FileSystem.readAsStringAsync(item.signature, { encoding: FileSystem.EncodingTypes.Base64 })
1 Like

This topic was automatically closed 15 days after the last reply. New replies are no longer allowed.