I'm using react-native-image-picker and react-native-s3-upload to upload images to s3 bucket.
RNS3.put(file, AWSOptions)
.then((response) => {
if (response.status !== 201) {
throw new Error('Failed to upload image to S3');
}
})
.catch(function (error) {
console.log('Error', JSON.stringify(error));
throw error;
});
and I get a response like this without image been uploaded.
Object {
"headers": Object {},
"status": 0,
"text": "Stream Closed",
}
I have exactly the same issue with a similar library called react-native-aws3.
It seems related to the new React Native version 0.62.2, because I've tried to upgrade today (my previous version was 0.61.5) and that library worked before.
I don't know if both libraries use a common API which now is broken in the new RN, but I suggest you to downgrade in the meanwhile.
I had the same issue and tried for days to fix it. This is what I came up with.
Make sure you follow the amplify guide and set up amplify init, amplify add auth and then do amplify push
Then do amplify add storage and follow the guide here (https://docs.amplify.aws/lib/storage/upload/q/platform/js). Then copy this code.
import Amplify, { Storage } from 'aws-amplify'
import config from './src/aws-exports'
// import awsconfig from './aws-exports';
// Might need to switch line 7 to awsconfig
Amplify.configure(config)
import { StatusBar } from 'expo-status-bar';
import React, { useState, useEffect } from 'react';
import { Button, Image, View, Platform, StyleSheet, Text } from 'react-native';
import * as ImagePicker from 'expo-image-picker';
function App() {
const [image, setImage] = useState(null)
useEffect(() => {
(async () => {
if (Platform.OS !== 'web') {
const { status } = await ImagePicker.requestMediaLibraryPermissionsAsync();
if (status !== 'granted') {
alert('Sorry, we need camera roll permissions to make this work!');
}
}
})();
}, []);
const pickImage = async () => {
let result = await ImagePicker.launchImageLibraryAsync({
mediaTypes: ImagePicker.MediaTypeOptions.All,
allowsEditing: true,
aspect: [4, 3],
quality: 1,
});
console.log(result)
async function pathToImageFile(data) {
try {
const response = await fetch(data);
const blob = await response.blob();
await Storage.put(`customer/images`, blob, {
contentType: 'image/jpeg', // contentType is optional
});
} catch (err) {
console.log('Error uploading file:', err);
}
}
// later
pathToImageFile(result.uri);
}
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Button title="Pick an image from camera roll" onPress={pickImage} />
{image && <Image source={{ uri: image }} style={{ width: 200, height: 200 }} />}
<Button title="Upload image" onPress={() => {alert(image)}} />
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'center',
},
});
export default withAuthenticator(App)```
I found an alternative module (aws-sdk) to upload files to the S3 bucket with RN 62 version.
Also, I have used base64-arraybuffer and react-native-fs modules well.
const s3bucket = new S3({
accessKeyId: XXXXXXXXXX,
secretAccessKey: XXXXXXXXXXXX,
Bucket: XXXXXXXXX,
signatureVersion: 'v4',
});
let contentType = 'image/jpeg';
let contentDeposition = 'inline;filename="' + file.name + '"';
const base64 = await fs.readFile(file.uri, 'base64');
const arrayBuffer = decode(base64);
s3bucket.createBucket(() => {
const params = {
Bucket: XXXXXXXXXXX,
Key: file.name,
Body: arrayBuffer,
ContentDisposition: contentDeposition,
ContentType: contentType,
};
s3bucket.upload(params, (err, data) => {
if (err) {
console.log('error in callback');
console.log(err);
setSpinner(false);
}
console.log('success');
console.log(data.Location);
setImageUrl(data.Location);
setSpinner(false);
});
});
The problem is with RN >= 0.62
You can either roll back to version < 0.62 or you can follow what I did.
I solved it by commenting out line number 43 in android/app/src/debug/java/**/ReactNativeFlipper.java
NetworkFlipperPlugin networkFlipperPlugin = new NetworkFlipperPlugin();
NetworkingModule.setCustomClientBuilder(
new NetworkingModule.CustomClientBuilder() {
#Override
public void apply(OkHttpClient.Builder builder) {
// builder.addNetworkInterceptor(new FlipperOkhttpInterceptor(networkFlipperPlugin)); <---- This line
}
});
client.addPlugin(networkFlipperPlugin);
client.start();
You will need to rebuild the app after doing this.
Hope this helps.
AWS S3 upload is a powerful service provided by React Native AWS amplify. The issue here might be caused by the React Native version or improper configuration of the package. You can check some amazing articles out there that will definitely help you integrate React Native AWS amplify properly based on your project configurations and make the AWS S3 upload working properly as well.
Related
We are trying to use Expo authentication with Okta as stated here:
https://docs.expo.dev/guides/authentication/#okta
Expo has very good documentation for lot's of stuff, but for the Okta authentication unfortunately we could not sort out how to use the library in a correct way.
Currently, with lot's of suffering (mostly because of the ambiguity in Okta's configuration pages), we came to a certain point where the following code correctly responds the code parameter. This is the exact same part from Expo documentation:
React.useEffect(() => {
if (response?.type === 'success') {
const { code } = response.params;
}
}, [response]);
But unfortunately we could not find any method how we can use the parameter code to get the scope information, email, name, etc...
Can anybody guide us how we can use the object code to retrieve these data? (The Okta documentation is not clear for this either, so we are stuck.)
Edit 1:
The response has the following structure:
response: {
"type": "success",
"error": null,
"url": "http://localhost:19006/?code=fUMjE4kBX2QZXXXXXX_XXXXXXXMQ084kEPrTqDa9FTs&state=3XXXXXXXXz",
"params": {
"code": "fUMjE4kBX2QZXXXXXX_XXXXXXXMQ084kEPrTqDa9FTs",
"state": "3XXXXXXXXz"
},
"authentication": null,
"errorCode": null
}
Edit 2:
Calling exchangeCodeAsync also yields errors.
Code:
const tokenRequestParams = {
code: code,
clientId: config.okta.clientId,
redirectUri: oktaRedirectUri,
extraParams: {
code_verifier: authRequest.codeVerifier
},
}
const tokenResult = await exchangeCodeAsync(tokenRequestParams, discovery);
Error:
TokenRequest.ts:205 Uncaught (in promise) Error: Client authentication failed (e.g., unknown client, no client authentication included, or unsupported authentication method). The authorization server MAY return an HTTP 401 (Unauthorized) status code to indicate which HTTP authentication schemes are supported. If the client attempted to authenticate via the "Authorization" request header field, the authorization server MUST respond with an HTTP 401 (Unauthorized) status code and include the "WWW-Authenticate" response header field matching the authentication scheme used by the client.
More info: Client authentication failed. Either the client or the client credentials are invalid.
at AccessTokenRequest.<anonymous> (TokenRequest.ts:205:1)
at Generator.next (<anonymous>)
at asyncGeneratorStep (asyncToGenerator.js:3:1)
at _next (asyncToGenerator.js:22:1)
PS: I asked the same question to the Expo forums also here. If we can solve there, I plan to reflect to here also for wider audience. (The method can be related with Okta rather than Expo itself.)
there are two ways to use Okta in React Native
1. By Restful APIs, using fetch/Axios
2. By Using Native SDK
By Restful APIs, using fetch/Axios
here is the full code of okta using restful
import React, { useState } from "react";
import {
ScrollView,
StyleSheet,
Text,
View,
TouchableOpacity,
Platform,
} from "react-native";
import {
useAutoDiscovery,
useAuthRequest,
makeRedirectUri,
exchangeCodeAsync,
} from "expo-auth-session";
import { maybeCompleteAuthSession } from "expo-web-browser";
import axios from "axios";
const oktaConfig = {
okta_issuer_url: "",
okta_client_id: "",
okta_callback_url: "com.okta.<OKTA_DOMAIN>:/callback",
};
export default App = (props) => {
const useProxy = true;
if (Platform.OS === "web") {
maybeCompleteAuthSession();
}
const discovery = useAutoDiscovery(oktaConfig.okta_issuer_url);
// When promptAsync is invoked we will get back an Auth Code
// This code can be exchanged for an Access/ID token as well as
// User Info by making calls to the respective endpoints
const [authRequest, response, promptAsync] = useAuthRequest(
{
clientId: oktaConfig.okta_client_id,
scopes: ["openid", "profile"],
redirectUri: makeRedirectUri({
native: oktaConfig.okta_callback_url,
useProxy,
}),
},
discovery
);
async function oktaCognitoLogin() {
const loginResult = await promptAsync({ useProxy });
ExchangeForToken(loginResult, authRequest, discovery);
}
return (
<View style={styles.container}>
<View style={styles.buttonContainer}>
<TouchableOpacity
style={styles.equalSizeButtons}
onPress={() => oktaCognitoLogin()}
>
<Text style={styles.buttonText}>Okta Login</Text>
</TouchableOpacity>
</View>
<ScrollView>
{response && <Text>{JSON.stringify(response, null, 2)}</Text>}
</ScrollView>
</View>
);
};
this is how, we can get exchange token and then get user info by using restful api
//After getting the Auth Code we need to exchange it for credentials
async function ExchangeForToken(response, authRequest, discovery) {
// React hooks must be used within functions
const useProxy = true;
const expoRedirectURI = makeRedirectUri({
native: oktaConfig.okta_callback_url,
useProxy,
})
const tokenRequestParams = {
code: response.params.code,
clientId: oktaConfig.okta_client_id,
redirectUri: expoRedirectURI,
extraParams: {
code_verifier: authRequest.codeVerifier
},
}
const tokenResult = await exchangeCodeAsync(
tokenRequestParams,
discovery
)
const creds = ExchangeForUser(tokenResult)
const finalAuthResult = {
token_res : tokenResult,
user_creds : creds
}
console.log("Final Result: ", finalAuthResult)
}
this is how we can get user info by using restful api
async function ExchangeForUser(tokenResult) {
const accessToken = tokenResult.accessToken;
const idToken = tokenResult.idToken;
//make an HTTP direct call to the Okta User Info endpoint of our domain
const usersRequest = `${oktaConfig.okta_issuer_url}/v1/userinfo`
const userPromise = await axios.get(usersRequest, {
headers: {
'Authorization': `Bearer ${accessToken}`
}
});
console.log(userPromise, "user Info");
}
const styles = StyleSheet.create({
container: {
margin: 10,
marginTop: 20,
},
buttonContainer: {
flexDirection: "row",
alignItems: "center",
margin: 5,
},
equalSizeButtons: {
width: "50%",
backgroundColor: "#023788",
borderColor: "#6df1d8",
flexDirection: "row",
justifyContent: "center",
alignItems: "center",
padding: 9,
borderWidth: 1,
shadowColor: "#6df1d8",
shadowOpacity: 8,
shadowRadius: 3,
shadowOffset: {
height: 0,
width: 0,
},
},
buttonText: {
color: "#ffffff",
fontSize: 16,
},
});
Reference Code
By Using Native SDK
for native SDK, you can use okta-react-native package like this
Login Screen
import React from 'react';
import {
Alert,
Button,
StyleSheet,
TextInput,
View,
ActivityIndicator
} from 'react-native';
import {
signIn,
introspectIdToken
} from '#okta/okta-react-native';
export default class CustomLogin extends React.Component {
constructor(props) {
super(props);
this.state = {
isLoading: false,
username: '',
password: '',
};
}
async componentDidMount() {
}
signInCustom = () => {
this.setState({ isLoading: true });
signIn({ username: this.state.username, password: this.state.password })
.then(() => {
introspectIdToken()
.then(idToken => {
this.props.navigation.navigate('ProfilePage', { idToken: idToken, isBrowserScenario: false });
}).finally(() => {
this.setState({
isLoading: false,
username: '',
password: '',
});
});
})
.catch(error => {
// For some reason the app crashes when only one button exist (only with loaded bundle, debug is OK) 🤦♂️
Alert.alert(
"Error",
error.message,
[
{
text: "Cancel",
onPress: () => console.log("Cancel Pressed"),
style: "cancel"
},
{ text: "OK", onPress: () => console.log("OK Pressed") }
]
);
this.setState({
isLoading: false
});
});
}
render() {
if (this.state.isLoading) {
return (
<View style={styles.container}>
<ActivityIndicator size="large" />
</View>
);
}
return (
<View style={styles.container}>
<TextInput
style={styles.input}
placeholder='Username'
onChangeText={input => this.setState({ username: input })}
testID="username_input"
/>
<TextInput
style={styles.input}
placeholder='Password'
onChangeText={input => this.setState({ password: input })}
testID="password_input"
/>
<Button
onPress={this.signInCustom}
title="Sign in"
testID='sign_in_button'
/>
<View style={styles.flexible}></View>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'center',
},
input: {
height: 40,
width: '80%',
margin: 12,
borderWidth: 1,
padding: 10,
},
flexible: {
flex: 1,
}
});
Profile Screen
import React from 'react';
import {
Text,
Button,
StyleSheet,
TextInput,
View,
} from 'react-native';
import {
signOut,
revokeAccessToken,
revokeIdToken,
clearTokens,
} from '#okta/okta-react-native';
export default class ProfilePage extends React.Component {
constructor(props) {
super(props);
this.state = {
idToken: props.route.params.idToken,
isBrowserScenario: props.route.params.isBrowserScenario
};
}
logout = () => {
if (this.state.isBrowserScenario == true) {
signOut().then(() => {
this.props.navigation.popToTop();
}).catch(error => {
console.log(error);
});
}
Promise.all([revokeAccessToken(), revokeIdToken(), clearTokens()])
.then(() => {
this.props.navigation.popToTop();
}).catch(error => {
console.log(error);
});
}
render() {
return (
<View style={styles.container}>
<Text testID="welcome_text">Welcome back, {this.state.idToken.preferred_username}!</Text>
<Button
onPress={this.logout}
title="Logout"
testID="logout_button"
/>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'center',
},
});
Reference Code
i can upload array of images to aws s3 succeefully without any issues, but i want to show these images in my application. I can upload images succeefuly to my s3 Bucket, but i want to add the metadata, or the url to dynamodb to show these images along with the other data in my application.
any support will be appreciated.
import { View, Text, Image, StyleSheet, ScrollView, Button, TextInput, Platform, PermissionsAndroid } from 'react-native';
import React, {useState, useEffect} from "react";
import { RadioButton } from 'react-native-paper';
import { useForm, Controller } from "react-hook-form";
import ImagePicker from 'react-native-image-crop-picker';
import DropDownPicker from 'react-native-dropdown-picker';
import { DataStore, Storage, API, graphqlOperation, Auth } from 'aws-amplify';
import { createService } from '../graphql/mutations';
import { RNS3 } from 'react-native-aws3';
const AddService = ()=>{
const [checked, setChecked] = React.useState('OFFERED');
const { control, handleSubmit, formState: { errors } } = useForm({
});
const [images, setImages]= useState([])
const uploadButtonClick = () => {
let promises = [];
images.map((image, i) => {
promises.push(uploadImageToS3(image));
});
}
const uploadImageToS3 = async image => {
const options = {
keyPrefix: "uploads/",
bucket: "alkhair-serviceimages142621-dev",
region: "us-east-1",
accessKey: "",
secretKey: "",
successActionStatus: 201
}
const file = {
uri: `${image.path}`,
name: image.path.substring(image.path.lastIndexOf('/') + 1), //extracting filename from image path
type: image.mime,
};
// I had to work around here to get the image url and add it to dynamoDB, but i can only add one image. I need to add the images that the user uploads max of 4 images
setImageUri(`http://${options.bucket}.s3.amazonaws.com/${options.keyPrefix}${file.name}`)
return new Promise((resolve, reject) => {
RNS3.put(file, options)
.then(res => {
if (res.status === 201) {
const {postResponse} = res.body;
resolve({
src: postResponse.location,
});
} else {
console.log('error uploading to s3', res);
}
})
.catch(err => {
console.log('error uploading to s3', err);
reject(err);
});
});
};
Promise.all(promises).then(uploadedImgs => {
console.log('Yayy, all images are uploaded successfully', uploadedImgs)
})
const onSubmit =async (data) =>{
const createNewService =
{
type: checked,
name: data.serviceName,
description: data.serviceDescription,
image: // i need to upload array of images here which is already uploaded to aws s3,
serviceProviderName: data.serviceProviderName,
serviceProviderAddress: data.serviceProviderAddress,
serviceProviderPhone: data.serviceProviderPhone,
notes: data.serviceProviderNotes,
serviceAreaId: areaLabel.value,
serviceCategoryId: categLabel.value,
}
API.graphql(graphqlOperation(createService, {input: createNewService}))
}
const pickImage = async()=>{
let isStoragePermitted = await requestExternalWritePermission();
if(isStoragePermitted){
ImagePicker.openPicker({
multiple: true,
waitAnimationEnd: false,
includeExif: true,
forceJpg: true,
compressImageQuality: 0.8,
maxFiles: 6,
includeBase64: true,
showsSelectedCount: true,
mediaType: 'photo',
}) .then(imgs=>{
if (imgs.length <= 6) {
setImages([...images, ...imgs]);
} else {
setImages([...images]);
ToastAndroid.show("Maximum of 6 images allowed", ToastAndroid.SHORT);
}
})}}
const [areaOpen, setAreaOpen] = useState(false);
const [areaValue, setAreaValue] = useState(null);
const [area, setArea] = useState([
{label: 'مصر القديمة', value: 'AAA'},
{label: 'المعادي', value: 'BBB'},
{label: 'القاهرة الجديدة', value: 'CCC'}
]);
const [serviceOpen, setServiceOpen] = useState(false);
const [serviceValue, setServiceValue] = useState(null);
const [service, setService] = useState([
{label: 'خدمات مواصلات ', value: '09339a8d'},
{label: 'اخرى', value: 'b4d227e3'}
]);
I'm trying to upload photos using React Native, AWS Amplify and S3. I'm getting the following error:
LOG [Error: URL.hostname is not implemented]
I've tried almost every method found here, and I haven't been able to come to any conclusions.
const uploadResource = async () => {
if (isLoading) return;
setisLoading(true);
const img = await fetchResourceFromURI(asset.uri);
return Storage.put(asset.uri, img, {
level: 'public',
contentType: asset.type,
progressCallback(uploadProgress) {
setProgressText(
`Progress: ${Math.round(
(uploadProgress.loaded / uploadProgress.total) * 100,
)} %`,
);
console.log(
`Progress: ${uploadProgress.loaded}/${uploadProgress.total}`,
);
},
})
.then(res => {
setProgressText('Upload Done: 100%');
setAsset(null);
setisLoading(false);
Storage.get(res.key)
.then(result => console.log(result))
.catch(err => {
setProgressText('Upload Error');
console.log(err);
});
})
.catch(err => {
setisLoading(false);
setProgressText('Upload Error');
console.log(err);
});
};
"aws-amplify": "^4.3.19",
"aws-amplify-react-native": "^6.0.4",
"react-native": "^0.68.2",
"aws-amplify": "^4.3.19",
"aws-amplify-react-native": "^6.0.4",
I tried to create an API for uploading & retrieving images with NestJS. Images should be stored on S3.
What I currently have:
Controller
#Post()
#UseInterceptors(FileFieldsInterceptor([
{name: 'photos', maxCount: 10},
]))
async uploadPhoto(#UploadedFiles() files): Promise<void> {
await this.s3Service.savePhotos(files.photos)
}
#Get('/:id')
#Header('content-type', 'image/jpeg')
async getPhoto(#Param() params,
#Res() res) {
const photoId = PhotoId.of(params.id)
const photoObject = await this.s3Service.getPhoto(photoId)
res.send(photoObject)
}
S3Service
async savePhotos(photos: FileUploadEntity[]): Promise<any> {
return Promise.all(photos.map(photo => {
const filePath = `${moment().format('YYYYMMDD-hhmmss')}${Math.floor(Math.random() * (1000))}.jpg`
const params = {
Body: photo.buffer,
Bucket: Constants.BUCKET_NAME,
Key: filePath,
}
return new Promise((resolve) => {
this.client.putObject(params, (err: any, data: any) => {
if (err) {
logger.error(`Photo upload failed [err=${err}]`)
ExceptionHelper.throw(ErrorCodes.SERVER_ERROR_UNCAUGHT_EXCEPTION)
}
logger.info(`Photo upload succeeded [filePath=${filePath}]`)
return resolve()
})
})
}))
}
async getPhoto(photoId: PhotoId): Promise<AWS.S3.Body> {
const object: S3.GetObjectOutput = await this.getObject(S3FileKey.of(`${Constants.S3_PHOTO_PATH}/${photoId.value}`))
.catch(() => ExceptionHelper.throw(ErrorCodes.RESOURCE_NOT_FOUND_PHOTO)) as S3.GetObjectOutput
logger.info(JSON.stringify(object.Body))
return object.Body
}
async getObject(s3FilePath: S3FileKey): Promise<S3.GetObjectOutput> {
logger.info(`Retrieving object from S3 s3FilePath=${s3FilePath.value}]`)
return this.client.getObject({
Bucket: Constants.BUCKET_NAME,
Key: s3FilePath.value
}).promise()
.catch(err => {
logger.error(`Could not retrieve object from S3 [err=${err}]`)
ExceptionHelper.throw(ErrorCodes.SERVER_ERROR_UNCAUGHT_EXCEPTION)
}) as S3.GetObjectOutput
}
The photo object actually ends up in S3, but when I download it I can't open it.
Same for the GET => can't be displayed.
What general mistake(s) I'm making here?
Not sure what values are you returning to your consumer and which values they use to get the Image again; Could you post how the actual response looks like, what is the request and verify, if the FQDN & Path match?
It seems you forgot about ACL as well, i.e. the resources you upload this way are not public-read by default.
BTW you could use aws SDK there:
import { Injectable } from '#nestjs/common'
import * as AWS from 'aws-sdk'
import { InjectConfig } from 'nestjs-config'
import { AwsConfig } from '../../config/aws.config'
import UploadedFile from '../interfaces/uploaded-file'
export const UPLOAD_WITH_ACL = 'public-read'
#Injectable()
export class ImageUploadService {
s3: AWS.S3
bucketName
cdnUrl
constructor(#InjectConfig() private readonly config) {
const awsConfig = (this.config.get('aws') || { bucket: '', secretKey: '', accessKey: '', cdnUrl: '' }) as AwsConfig // read from envs
this.bucketName = awsConfig.bucket
this.cdnUrl = awsConfig.cdnUrl
AWS.config.update({
accessKeyId: awsConfig.accessKey,
secretAccessKey: awsConfig.secretKey,
})
this.s3 = new AWS.S3()
}
upload(file: UploadedFile): Promise<string> {
return new Promise((resolve, reject) => {
const params: AWS.S3.Types.PutObjectRequest = {
Bucket: this.bucketName,
Key: `${Date.now().toString()}_${file.originalname}`,
Body: file.buffer,
ACL: UPLOAD_WITH_ACL,
}
this.s3.upload(params, (err, data: AWS.S3.ManagedUpload.SendData) => {
if (err) {
return reject(err)
}
resolve(`${this.cdnUrl}/${data.Key}`)
})
})
}
}
For anyone having the same troubles, I finally figured it out:
I enabled binary support on API Gateway (<your-gateway> Settings -> Binary Media Types -> */*) and then returned all responses from lambda base64 encoded. API Gateway will do the decode automatically before returning the response to the client.
With serverless express you can can enable the auto base64 encoding easily at the server creation:
const BINARY_MIME_TYPES = [
'application/javascript',
'application/json',
'application/octet-stream',
'application/xml',
'font/eot',
'font/opentype',
'font/otf',
'image/jpeg',
'image/png',
'image/svg+xml',
'text/comma-separated-values',
'text/css',
'text/html',
'text/javascript',
'text/plain',
'text/text',
'text/xml',
]
async function bootstrap() {
const expressServer = express()
const nestApp = await NestFactory.create(AppModule, new ExpressAdapter(expressServer))
await nestApp.init()
return serverlessExpress.createServer(expressServer, null, BINARY_MIME_TYPES)
}
In the Controller, you're now able to just return the S3 response body:
#Get('/:id')
async getPhoto(#Param() params,
#Res() res) {
const photoId = PhotoId.of(params.id)
const photoObject: S3.GetObjectOutput = await this.s3Service.getPhoto(photoId)
res
.set('Content-Type', 'image/jpeg')
.send(photoObject.Body)
}
Hope this helps somebody!
I am currently making an application using react-native, expo, and AWS. I use Cognito for authentification and I have it set up such that I can successfully mutate and query a database in DynamoDB.
I am now attempting to write code such that I can store the pictures for the users of my application in S3 and store the path to that picture in DynamoDB. However, upon running the following:
const result2 = await Storage.get('test.txt')
console.log(result2)
The log gives me a link that takes me to what appears to be an xml file that reads:
This XML file does not appear to have any style information associated with it. The document tree is shown below.
<Error>
<Code>InvalidToken</Code>
<Message>
The provided token is malformed or otherwise invalid.
</Message>
<Token-0>
AgoJb3JpZ2luX2VjEA4aCXVzLWVhc3QtMiJIMEYCIQD/DyNL3qxQ3XrXBXsOlH2ojEks6sfdN9Vp7rPT5K9JFQIhAI2lEAmu6ivQIQKNtAo1VqFuZgdld5S/DrfcElNsVDo/Kq8FCOf//////////wEQABoMODM3Njc2NjA0ODkyIgwj9ikFX4w7POTaQlEqgwXkzZRq7kCqo8W0YaAD6fT7sJ4nok4nxzBO58frPZ4a1nSCESuwq/VShJZMQAMDkDPKci4vV v aJMhPOJXvtRjkoIQ5lGaCQAqYMXDeU/RFOJosmekNZ 0vZGu/X9KWfcNFhJaz7WW5FDl3N3u BGQdBzUjNhCrl01HMpKyL5F0HFB1mq4l H9d FyvPD4 LStyNOXYkGKf7X34hEZdccRkbjDyzfG4hBmv64JGiJfgh5 dD6IlNdNsqkw7N1Uw26Q7mf3erhOrwJwLIDyYC77LW3HU9uLqxQBH0MiTxJBcWW47BT4xENuSHZ136zRazgjHYcin Sr1NyCKwI4e2BbJ//Fudi Jnjf/QDPgYmSrpOO/0p5YGejpPYoZ6cqFy7V8xuI82YK/CZTc5hCOBMvo7NyPa2lsGkDXcfh IknmVEH5OXHhM0FPGM/U6L0ikBUYJgqqiVqByxdrUyYt F6FxapyCKgWcBCXwQUq28KoFvhIqXFbc5hlkGxFdUvIK2WWtMpDntcvU6hgivXoRRGKh880vkQV6bQsF3AaniH/El0EcI5jGF5aiciNZo9wNuMKMsA8w7bpT hBDETHeGOd3lenGNvgazdwWKnnhqN3QqwaSPFjd0Q41wNYOH8ySmP0S0/UbYCTIwRDx1o4FX6UI2THnS8nOvbtEG32QHDpqY3gpL00tCLGO4S9BFtkIC2TbhvX7jfF3bAbfvjEVOFhENT0K v OYq02Ig4dlMa4DdnPexEmy666R8Pbf 7fTCP2wzWDXjfxJjxkKx8gfsTmUOgDgUPb2p8azxSXzJr1Rg5 zhhX0DuVepg5z66HQS23RwAhIkOrXEzi95nPbd9NAOMNPw0OwFOrMB4lBUn04vbQAqDiKOe91AZR64351xwrn0jp5zZ YwumK9Tk/3rY/ N2ibYoiHdfOM3Wg4Ucm2 qCDWtJIkbvCAXlhjrpUZLmV6dJATJ417OzlUlJfsK04y7K8uNrGSFwbWYw3/gblaggupxQc7TrpmhKCIXHVIdZZmawMH5vfULkhHQL/FiQKdt13znIZ/hHGs1jc3cC9Z NmhMIXtufNlIGvKmWzNCq8LegxNzoZtlXqVWM=
</Token-0>
<RequestId>5186B48D259D1B01</RequestId>
<HostId>
RJptwOZEArdnFf7Zmk7Od8oFWh68GeR8ey/O13M39JuVxSScYyKXVVVsXwa+37Wus1M/DHttry8=
</HostId>
</Error>
For reference, the rest of my App.js file reads:
/*
General
*/
import React from 'react';
import { Platform, Component, StyleSheet, Text, View, TextInput, Button, TouchableOpacity } from 'react-native';
/*
Amplify
*/
import Amplify from '#aws-amplify/core'
import config from './src/aws-exports'
Amplify.configure({
...config,
Analytics: { //Needed to disable possible promise reject error from analytics
disabled: true
}
});
/*
Authentification
*/
import Auth from '#aws-amplify/auth'
import { withAuthenticator } from 'aws-amplify-react-native'
/*
Database
*/
import API, { graphqlOperation } from '#aws-amplify/api'
import {Storage} from 'aws-amplify'
import * as queries from './src/graphql/queries';
import * as mutations from './src/graphql/mutations';
import * as subscriptions from './src/graphql/subscriptions';
import {v4 as uuid} from 'uuid';
class App extends React.Component {
state = {
name: "",
User: []
}
onChangeText = (key, val) => {
this.setState({ [key]: val })
}
addUser = async event => {
const { name, User } = this.state
event.preventDefault()
const information = {
username: name,
}
const result = await API.graphql(graphqlOperation(mutations.createUser, {input: information}))
const newUser = result.data.createUser
const updatedUser = [newUser, ...User]
this.setState({ User: updatedUser, name: "" })
/*
Adding Files Test
*/
const result2 = await Storage.get('test.txt')
console.log(result2)
}
render() {
return (
<View style={styles.container}>
<TextInput
style={styles.input}
value={this.state.name}
onChangeText={val => this.onChangeText("name", val)}
placeholder='Add a User'
/>
<TouchableOpacity onPress={this.addUser} style={styles.buttonContainer}>
<Text style={styles.buttonText}>Add +</Text>
</TouchableOpacity>
</View>
);
}
}
export default withAuthenticator(App, { includeGreetings: true })
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: "#fff",
paddingHorizontal: 10,
paddingTop: 50
},
input: {
height: 50,
borderBottomWidth: 2,
borderBottomColor: "blue",
marginVertical: 10
},
buttonContainer: {
backgroundColor: "#34495e",
marginTop: 10,
marginBottom: 10,
padding: 10,
borderRadius: 5,
alignItems: "center"
},
buttonText: {
color: "#fff",
fontSize: 24
}
})
What would this error mean? Am I simply giving get() the wrong type of input? I do have a file called "test.txt" in the top level of my S3.