It's been a joruney trying to get simple file uploads to an AWS S3 bucket working from my react native app built with expo.
Current problem: Upon attempting the upload, I get an error saying:
Failed to construct URL with https://[bucket name].s3.us-east-1.amazonaws.com [TypeError: Cannot read property 'decode' of undefined]
.
I had to install react-native-url-polyfill/auto
and import it in my App.js file to get past a previous URL error, as suggested here. I also had to do some funky configurations for babel and Metro due to this issue
My code (with styling and a bunch of other components stripped out to make it a little simpler):
import { View, Modal, Pressable, Text, TextInput, Image, Alert } from 'react-native';
import { useEffect, useContext, useState } from 'react';
import { Button, Icon } from '@rneui/themed';
import { S3Client, PutObjectCommand } from '@aws-sdk/client-s3';
import * as ImagePicker from 'expo-image-picker';
import { AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, S3_REGION, S3_BUCKET } from '@env';
const Memories = () => {
const [modalVisible, setModalVisible] = useState(false);
const [permissionStatus, requestPermission] = ImagePicker.useCameraPermissions();
const client = new S3Client({
credentials: {
accessKeyId: AWS_ACCESS_KEY_ID,
secretAccessKey: AWS_SECRET_ACCESS_KEY,
},
region: S3_REGION,
});
const pickImage = async () => {
// No permissions request is necessary for launching the image library
let result = await ImagePicker.launchImageLibraryAsync({
mediaTypes: ImagePicker.MediaTypeOptions.Images,
allowsEditing: true,
aspect: [4, 3],
quality: 1,
});
console.log(result);
if (!result.canceled) {
const awsData = await handleUpload(result.assets[0]).catch((err) => console.log(err));
console.log(awsData);
}
};
const handleUpload = async (pickerResult) => {
try {
const res = await fetch(pickerResult.uri);
const imgBlob = await res.blob();
const data = await client.send(
new PutObjectCommand({
ACL: 'public-read',
Bucket: S3_BUCKET,
Key: `usergen-${Date.now().toString()}`,
Body: imgBlob,
})
);
return Promise.resolve(data);
} catch (err) {
console.log(err);
return Promise.reject(err);
}
};
return (
<View>
<Modal
animationType='slide'
transparent={true}
visible={modalVisible}
onRequestClose={() => {
setModalVisible(!modalVisible);
}}>
<View style={styles.centeredView}>
<View style={styles.modalView}>
<TextInput
multiline
placeholder='Body Text'
value={bodyText}
onChangeText={setBodyText}
style={styles.textInput}
/>
<View>
<Button style={styles.btnGroup} onPress={pickImage}>
Upload from Camera Roll
</Button>
</View>
<Pressable
style={[styles.button, styles.buttonClose]}
onPress={() => setModalVisible(!modalVisible)}>
<Text style={styles.textStyle}>Cancel</Text>
</Pressable>
</View>
</View>
</Modal>
<Button
onPress={() => setModalVisible(!modalVisible)}>
<Icon type='ionicons' name='add' color='white' />
</Button>
</View>
);
};
export default Memories;
As I mentioned, this results in the following console output immediately after completing the image pick process:
Failed to construct URL with https://[bucket name].s3.us-east-1.amazonaws.com [TypeError: Cannot read property 'decode' of undefined]
LOG [TypeError: Cannot read property 'decode' of undefined]
LOG [TypeError: Cannot read property 'decode' of undefined]
LOG undefined
Any help, insights, resources, etc. would be greatly appreciated. This is my first time working with AWS in general. Has not been easy.
Just in case anyone comes along this with the same issue, this ended up being how I got past the problem:
I followed this solution instead of the babel & Metro configuration changes suggested elsewhere in that Github issue thread. I needed to apply an identical patch to @aws-sdk/middleware-flexible-checksums
in addition to the one suggested there.
The polyfills (react-native-url-polyfill
and react-native-get-random-values
) were still required and now work without issue.
I really have no clue what caused this weird chain of errors, n'or do I ever want to think about the 5+ hours I sunk into this issue again! :)