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.
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
Please, am new to react native. Am trying to get the current location through google places autocomplete for android in expo but an getting the error 'TypeError: null is not an object (evaluating 'RNFusedLocation.getCurrentPosition')'
below is my code
Please, am new to react native. Am trying to get the current location through google places autocomplete for android in expo but an getting the error 'TypeError: null is not an object (evaluating 'RNFusedLocation.getCurrentPosition')'
below is my code
App.js
import { StyleSheet, Text, View, StatusBar, PermissionsAndroid, Platform,} from 'react-native';
import HomeScreen from './src/screens/HomeScreen/HomeScreen';
import DestinationSearch from './src/screens/DestinationSearch/DestinationSearch';
import SearchResults from './src/screens/SearchResults/SearchResults';
import { useEffect, useState } from 'react';
//import * as Location from 'expo-location';
import Geolocation, { getCurrentPosition } from 'react-native-geolocation-service';
navigator.geolocation = require('react-native-geolocation-service');
export default function App() {
const androidPermission = async () => {
try {
const granted = await PermissionsAndroid.request(
PermissionsAndroid.PERMISSIONS.ACCESS_FINE_LOCATION,
{
title: "Uber App location Permission",
message:
"Uber App needs access to your location " +
"so you can take awesome rides.",
buttonNeutral: "Ask Me Later",
buttonNegative: "Cancel",
buttonPositive: "OK"
}
);
if (granted === PermissionsAndroid.RESULTS.GRANTED) {
console.log("You can use the location");
} else {
console.log("Location permission denied");
}
} catch (err) {
console.warn(err);
}
};
useEffect(() => {
if (androidPermission) {
Geolocation.getCurrentPosition(
(position) => {
console.log(position);
},
(error) => {
// See error code charts below.
console.log(error.code, error.message);
},
{ enableHighAccuracy: true, timeout: 15000, maximumAge: 10000 }
);
}
}, [])
useEffect(() => {
if(Platform.OS == 'android') {
androidPermission()
} else{
//IOS
Geolocation.requestAuthorization();
}
}, [])
return (
<View>
{/* <HomeScreen /> */}
<DestinationSearch />
{/* <SearchResults /> */}
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'center',
marginTop:StatusBar.currentHeight
},
});
`
DestinationSearch.jsx
import { View, Text, StyleSheet, SafeAreaView, StatusBar } from 'react-native'
import React, {useEffect, useState,} from 'react'
import { GooglePlacesAutocomplete } from 'react-native-google-places-autocomplete';
import {GOOGLE_MAPS_APIKEY} from '#env'
import PlaceRow from './PlaceRows';
const DestinationSearch = () => {
const [originPlace,setOriginPlace] = useState(null)
const [destinationPlace, setDestinationPlace] = useState(null)
useEffect(() => {
console.log('useEffect is called')
if (originPlace && destinationPlace) {
console.warn('Redirect to results')
}
}, [originPlace, destinationPlace])
return (
<SafeAreaView>
<View style={styles.container}>
<GooglePlacesAutocomplete
nearbyPlacesApi = 'GooglePlacesSearch'
placeholder = 'From...'
listViewDisplayed = 'auto'
debounce = {400}
currentLocation = {true}
currentLocationLabel='Current location'
minLenght = {2}
enabledPoweredByContainer = {true}
fetchDetails = {true}
autoFoccus = {true}
renderRow={(data)=> <PlaceRow data={data}/>}
query ={{
key: GOOGLE_MAPS_APIKEY ,
language :'en'
}}
styles={{
container: styles.autocompleteContainer,
textInput: styles.textInput,
listView: styles.listView,
seperator: styles.separator
}}
onPress = {(data, details = null)=> {
setOriginPlace({data, details})
console.log(currentLocation)
}}
/>
<GooglePlacesAutocomplete
nearbyPlacesApi = 'GooglePlacesSearch'
placeholder = 'To...'
listViewDisplayed = 'auto'
debounce = {400}
minLenght = {2}
enabledPoweredByContainer = {true}
fetchDetails = {true}
autoFoccus = {true}
query ={{
key: GOOGLE_MAPS_APIKEY ,
language :'en'
}}
renderRow={(data)=> <PlaceRow data={data}/>}
styles={{
container: {
...styles.autocompleteContainer,
top: 70
},
textInput: styles.textInput,
seperator: styles.separator
}}
onPress = {(data, details = null)=> {
setDestinationPlace({data, details})
}}
/>
I want to use Expo Image Picker, but I'm getting the following error.
'Console Warning : possible unhandled promise rejection (id : 0)'.
I already installed 'expo-image-picker'.
Here's the promise code, I don't see what's wrong here, any ideas?
import { StatusBar } from 'expo-status-bar';
import React, { useState, useEffect } from 'react';
import { Platform, StyleSheet, Text, View, Button,Image } from 'react-native';
import * as ImagePicker from 'expo-image-picker';
import Constants from 'expo-constants';
export default function App() {
const [image,setImage] = useState(null);
useEffect(async () => {
if(Platform.OS !== 'web'){
const {status} = await ImagePicker.requestMediaLibraryPermissionsAsync();
if (status !== 'granted'){
alert('Permisson denied!')
}
}
}, []);
const PickImage = async() => {
let result = await ImagePicker.launchImageLibraryAsync({
mediaTypes : ImagePicker.mediaTypesOptions.All,
allowsEditing:true,
aspect:[4,3],
quality:1
})
console.log(result)
if(!result.cancelled ){
setImage(result.uri)
}
}
return (
<View style={styles.container}>
<Button title = "Choose Image" onPress = {PickImage}/>
{image && <Image source={{uri:image}} style = {{
width : 200,
height:200
}}/>}
<StatusBar style="auto" />
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'center',
},
});
Assigning properties to mediatypes is incorrect.
Correction: mediaTypes:ImagePicker.MediaTypeOptions.All,
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 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.