0

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.

Jordy337
  • 390
  • 2
  • 5
  • 17
  • sorry, what is value of `S3_BUCKET`? it's not `undefined`, right? – willisc Aug 27 '23 at 22:21
  • @willisc It's an environment variable defined as the bucket's name. Definitely working at least there because it's included in the error (I put it in as [bucket name] just to hide it because it is a public bucket with stuff in it) – Jordy337 Aug 27 '23 at 22:23
  • noticed this line https://github.com/aws/aws-sdk-js-v3/blob/d7d2a45b32b4194bd9d05b078174e5a261e7b531/packages/util-endpoints/src/utils/getEndpointUrl.ts#L11 and was thinking if you can print the `expression` variable in the lib, maybe you get the chance to figure it out. – willisc Aug 27 '23 at 22:43
  • @willisc I just tried slapping a `new URL("https://test.com")` in the middle of my code to test it out and I found it threw an identical error. This must point to an issue with that polyfill I had to use. Thanks for the nudge, I will keep diving into it. – Jordy337 Aug 27 '23 at 23:20
  • Cool! glad you found the identical error. – willisc Aug 28 '23 at 08:02

1 Answers1

0

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! :)

Jordy337
  • 390
  • 2
  • 5
  • 17