QuaggaJS Barcode scanner not working for Android devices - javascript

I have created a barcode scanner in my react component using QuaggaJS. The scanner is meant to be used on mobile devices through the web view. It's working fine on all iPhone devices but is not scanning correctly on androids. I am guessing this is a resolution issue in the constraints but have not found a solution that works.
import React, { useEffect, useState } from "react";
import { useNavigate } from "react-router-dom";
import Quagga from "#ericblade/quagga2";
import adapter from "webrtc-adapter";
import LogService from "../../services/LogService";
import "./BarcodeScanner.css";
const BarcodeScanner = (props) => {
const navigate = useNavigate();
const logService = new LogService();
let mainCameraDeviceId = "";
useEffect(() => {
async function getBackDevices() {
await Quagga.CameraAccess.request();
let devices = await Quagga.CameraAccess.enumerateVideoDevices()
.then((devices) => devices)
.catch((err) => {
logService.Log("Error", "Error when enumerating video devices", err);
console.error(`${err.name}: ${err.message}`);
});
let backDevices = [];
devices.forEach((device) => {
logService.Log("Debug", "Detected Device",device);
if(device.kind.toLowerCase().includes("videoinput") && device.label.toLowerCase().includes("back")) {
backDevices.push(device);
console.log(`${device.kind}: ${device.label} id = ${device.deviceId}`);
}
});
if (backDevices.length == 0 && devices.length == 1) {
backDevices.push(devices[0]);
}
logService.Log("Debug", "Detected back devices", backDevices);
mainCameraDeviceId = backDevices[backDevices.length-1].deviceId;
startQuagga();
}
getBackDevices();
}, []);
function startQuagga() {
logService.Log("Debug", "Selected camera device", mainCameraDeviceId);
let customConstraints = {
focusMode: 'continuous',
facingMode: "environment",
zoom: {min: 1.5, max: 2},
deviceId: mainCameraDeviceId,
width: 640,
height: 480
}
if(props.deviceType == "iPhone") {
customConstraints.width = { min: 480 };
customConstraints.height = { min: 1281 };
}
logService.Log("Debug", "Device Type", props.deviceType);
logService.Log("Debug", "Quagga constraints", customConstraints);
try {
Quagga.init(
{
inputStream: {
name: "Live",
type: "LiveStream",
target: document.querySelector("#interactive"),
constraints: customConstraints
},
locate: true,
decoder: {
readers: [
"upc_reader"
],
multiple: false
},
},
function (err) {
if (err != null) {
console.log(err);
props.onError(err);
stopScanner();
return;
}
console.log("Initialization finished. Ready to start");
Quagga.start();
}
);
} catch {
props.onError("Failed to open camera");
}
}
Quagga.onDetected((data) => {
props.onDetected(data.codeResult.code);
Quagga.stop();
});
const stopScanner = () => {
console.log("stopping Quagga");
Quagga.stop();
};
return (
<div
className="barcode-scanner viewport"
id="interactive"
>
<div className="box-overlay"></div>
</div>
);
};
export default BarcodeScanner;

Related

React native undefined is not an object error

I have a MQTTConnection.js class inside a src folder this class defines methods like subscribe, unsubsribe and connect. Also the App.js class implements the methods imported from MQTTConnection.js and defines the prototypes functions that start with ONMQTT. When I try to run the project the next error is displayed : TypeError: _this.onMQTTLost is not a function.
App.js
import MQTTConnection from './src/MQTTConnection'
import React, {
useEffect
} from 'react'
import {
View,
StyleSheet,
Text,
Button
} from 'react-native'
import {
Buffer
} from 'buffer';
global.Buffer = Buffer;
export default function App() {
useEffect(() => {
this.mqttConnect = new MQTTConnection()
this.mqttConnect.onMQTTConnect = this.onMQTTConnect
this.mqttConnect.onMQTTLost = this.onMQTTLost
this.mqttConnect.onMQTTMessageArrived = this.onMQTTMessageArrived
this.mqttConnect.onMQTTMessageDelivered = this.onMQTTMessageDelivered
onMQTTConnect = () => {
console.log('App onMQTTConnect')
this.mqttConnect.subscribeChannel('hanth2')
}
onMQTTLost = () => {
console.log('App onMQTTLost')
}
onMQTTMessageArrived = (message) => {
console.log('App onMQTTMessageArrived: ', message);
console.log('App onMQTTMessageArrived payloadString: ', message.payloadString);
}
onMQTTMessageDelivered = (message) => {
console.log('App onMQTTMessageDelivered: ', message);
}
this.mqttConnect.connect('4924d328ffbe4e649e261db3563eed0a.s2.eu.hivemq.cloud', 8884)
return () => {
this.mqttConnect.close()
}
}, [])
return ( <
View style = {
styles.container
} >
<
Text > react_native_mqtt < /Text> <
Button title = "Press me"
onPress = {
() => this.mqttConnect.send('hanth2', "message send to channel hanth2 again")
}
/> <
/View>
)
}
const styles = StyleSheet.create({
container: {
flex: 1,
alignItems: 'center',
justifyContent: 'center'
}
})
MQTTConnection.js
import init from 'react_native_mqtt';
import uuid from 'react-native-uuid';
import AsyncStorage from '#react-native-community/async-storage';
init({
size: 10000,
storageBackend: AsyncStorage,
defaultExpires: 1000 * 3600 * 24,
enableCache: true,
sync: {},
});
const defaultConnectOptions = {
reconnect: false,
cleanSession: true,
mqttVersion: 3,
keepAliveInterval: 60,
timeout: 60,
userName: 'serjaumen22',
password: ''
}
export default class MQTTConnection {
constructor() {
this.mqtt = null;
this.QOS = 0;
this.RETAIN = true;
}
connect(host, port, options = null) {
if (options) {
this.QOS = options.qos;
this.RETAIN = options.retain;
}
let currentTime = +new Date();
let clientID = currentTime + uuid.v1();
clientID = clientID.slice(0, 23);
console.log('clientID: ', clientID)
this.mqtt = new Paho.MQTT.Client(host, port, clientID);
this.mqtt.onConnectionLost = (res) => {
this.onMQTTLost;
};
this.mqtt.onMessageArrived = (message) => {
this.onMQTTMessageArrived(message);
};
this.mqtt.onMessageDelivered = (message) => {
this.onMQTTMessageDelivered(message);
};
const connectOptions = options ? options : defaultConnectOptions;
this.mqtt.connect({
onSuccess: this.onMQTTSuccess,
onFailure: this.onMQTTFailure,
...connectOptions
});
}
onMQTTSuccess = () => {
this.onMQTTConnect()
}
onMQTTFailure = () => {
this.onMQTTLost()
}
subscribeChannel(channel) {
console.log('MQTTConnection subscribeChannel: ', channel)
if (!this.mqtt || !this.mqtt.isConnected()) {
return;
}
this.mqtt.subscribe(channel, this.QOS);
}
unsubscribeChannel(channel) {
console.log('MQTTConnection unsubscribeChannel: ', channel)
if (!this.mqtt || !this.mqtt.isConnected()) {
return;
}
this.mqtt.unsubscribe(channel);
}
send(channel = null, payload) {
console.log('MQTTConnection send: ')
if (!this.mqtt || !this.mqtt.isConnected()) {
return;
}
if (!channel || !payload) {
return false;
}
console.log(`MQTTConnection send publish channel: ${channel}, payload: ${payload} qos: ${this.QOS} retained: ${this.RETAIN}`)
this.mqtt.publish(channel, payload, this.QOS, this.RETAIN);
}
close() {
this.mqtt && this.mqtt.disconnect();
this.mqtt = null;
}
}
MQTTConnection.prototype.onMQTTConnect = null
MQTTConnection.prototype.onMQTTLost = null
MQTTConnection.prototype.onMQTTMessageArrived = null
MQTTConnection.prototype.onMQTTMessageDelivered = null

AsyncStorage is not working, the value always disappears when the application reload

I want to save an array of object to device using AsyncStorage, I have used AsyncStorage for save my jwt token and it working well. But when i tried to save an array of object it wasn't working here is my code :
const storeCart = async (value) => {
try {
const jsonValue = JSON.stringify(value);
await AsyncStorage.setItem(`cart-${user.id}`, jsonValue);
} catch (e) {
console.log(e);
} finally {
const jsonValue = await AsyncStorage.getItem(`cart-${user.id}`);
if (jsonValue != null) {
console.log(
'this is from async storage after save',
JSON.parse(jsonValue),
user.id,
);
}
}
};
const getCart = async () => {
try {
const jsonValue = await AsyncStorage.getItem(`cart-${user.id}`);
if (jsonValue != null) {
setCarts(JSON.parse(jsonValue));
console.log('carts after refresh the app', jsonValue, user.id);
}
} catch (e) {
console.log(e);
}
};
I have tried to console log the result after setItem, and it was saved successly, but when i reload the app and tried to console.log, it return and empty array, the key is already correct, and i have console log the user id too for make sure.
Here's the full code, if needed :
import React, { useState, createContext, useEffect, useContext } from 'react';
import AsyncStorage from '#react-native-async-storage/async-storage';
import AuthenticationContext from '../authentication/AuthenticationContext';
const CartsContext = createContext();
export const CartsContextProvider = ({ children }) => {
const { user } = useContext(AuthenticationContext);
const [carts, setCarts] = useState([]);
const storeCart = async (value) => {
try {
const jsonValue = JSON.stringify(value);
await AsyncStorage.setItem(`cart-${user.id}`, jsonValue);
} catch (e) {
console.log(e);
} finally {
const jsonValue = await AsyncStorage.getItem(`cart-${user.id}`);
if (jsonValue != null) {
console.log(
'this is from async storage after save',
JSON.parse(jsonValue),
user.id,
);
}
}
};
const getCart = async () => {
try {
const jsonValue = await AsyncStorage.getItem(`cart-${user.id}`);
if (jsonValue != null) {
setCarts(JSON.parse(jsonValue));
console.log('carts after refresh the app', jsonValue, user.id);
}
} catch (e) {
console.log(e);
}
};
useEffect(() => {
storeCart(carts);
}, [carts, user]);
useEffect(() => {
getCart();
}, [user]);
const searchByMerchant = (merchantId) => {
for (let i = 0; i < carts.length; i++) {
if (carts[i].merchantId === merchantId) {
return carts[i];
}
}
};
const searchByItem = (itemId, arrayOfItems) => {
for (let i = 0; i < arrayOfItems.length; i++) {
if (itemId === arrayOfItems[i].productId) {
return arrayOfItems[i];
}
}
};
const deletePerMerchant = (merchantId) => {
return carts.filter((x) => {
return x.merchantId != merchantId;
});
};
const deletePerItem = (itemId, arrayOfItems) => {
return arrayOfItems.filter((x) => {
return x.productId != itemId;
});
};
const addItem = (merchantId, productId, qty) => {
let merchantCheck = searchByMerchant(merchantId);
let temp = null;
if (merchantCheck) {
let itemCheck = searchByItem(productId, merchantCheck.items);
if (itemCheck) {
let itemAfterRemoveSelectedItem = deletePerItem(
productId,
merchantCheck.items,
);
temp = deletePerMerchant(merchantId);
if (qty === 0) {
if (itemAfterRemoveSelectedItem.length === 0) {
setCarts([...temp]);
} else {
setCarts([
...temp,
...[
{
merchantId,
items: [...itemAfterRemoveSelectedItem],
},
],
]);
}
} else {
setCarts([
...temp,
...[
{
merchantId,
items: [
...itemAfterRemoveSelectedItem,
...[{ productId, qty: qty }],
],
},
],
]);
}
} else {
temp = deletePerMerchant(merchantId);
setCarts([
...temp,
...[
{
merchantId,
items: [...merchantCheck.items, ...[{ productId, qty }]],
},
],
]);
}
} else {
if (qty > 0) {
setCarts([...carts, ...[{ merchantId, items: [{ productId, qty }] }]]);
}
}
};
return (
<CartsContext.Provider value={{ carts, addItem }}>
{children}
</CartsContext.Provider>
);
};
export default CartsContext;
Thanks !
I believe this is happening because the cart value is being overwritten when you reload the app because the useEffect is called each time you reload the app.
The setCarts is being called after adding something in cart, and therefore the first useEffect (which has in deps [cart, user]) is being called too and it sets correctly the data in local storage. But afterwards, if you reload the app, the same useEffect is being called again and the cart = [] is being set into the local storage.
I would solve this by giving up to the first useEffect and setting directly the data into local storage without having any state related to it.
import React, { useState, createContext, useEffect, useContext } from 'react';
import AsyncStorage from '#react-native-async-storage/async-storage';
import AuthenticationContext from '../authentication/AuthenticationContext';
const CartsContext = createContext();
export const CartsContextProvider = ({ children }) => {
const { user } = useContext(AuthenticationContext);
const storeCart = async (value) => {
try {
const jsonValue = JSON.stringify(value);
await AsyncStorage.setItem(`cart-${user.id}`, jsonValue);
} catch (e) {
console.log(e);
} finally {
const jsonValue = await AsyncStorage.getItem(`cart-${user.id}`);
if (jsonValue != null) {
console.log(
'this is from async storage after save',
JSON.parse(jsonValue),
user.id,
);
}
}
};
const getCart = async () => {
try {
const jsonValue = await AsyncStorage.getItem(`cart-${user.id}`);
if (jsonValue != null) {
storeCart(JSON.parse(jsonValue));
console.log('carts after refresh the app', jsonValue, user.id);
}
} catch (e) {
console.log(e);
}
};
useEffect(() => {
getCart();
}, [user]);
const searchByMerchant = (merchantId) => {
for (let i = 0; i < carts.length; i++) {
if (carts[i].merchantId === merchantId) {
return carts[i];
}
}
};
const searchByItem = (itemId, arrayOfItems) => {
for (let i = 0; i < arrayOfItems.length; i++) {
if (itemId === arrayOfItems[i].productId) {
return arrayOfItems[i];
}
}
};
const deletePerMerchant = (merchantId) => {
return carts.filter((x) => {
return x.merchantId != merchantId;
});
};
const deletePerItem = (itemId, arrayOfItems) => {
return arrayOfItems.filter((x) => {
return x.productId != itemId;
});
};
const addItem = (merchantId, productId, qty) => {
let merchantCheck = searchByMerchant(merchantId);
let temp = null;
if (merchantCheck) {
let itemCheck = searchByItem(productId, merchantCheck.items);
if (itemCheck) {
let itemAfterRemoveSelectedItem = deletePerItem(
productId,
merchantCheck.items,
);
temp = deletePerMerchant(merchantId);
if (qty === 0) {
if (itemAfterRemoveSelectedItem.length === 0) {
storeCart([...temp]);
} else {
storeCart([
...temp,
...[
{
merchantId,
items: [...itemAfterRemoveSelectedItem],
},
],
]);
}
} else {
storeCart([
...temp,
...[
{
merchantId,
items: [
...itemAfterRemoveSelectedItem,
...[{ productId, qty: qty }],
],
},
],
]);
}
} else {
temp = deletePerMerchant(merchantId);
storeCart([
...temp,
...[
{
merchantId,
items: [...merchantCheck.items, ...[{ productId, qty }]],
},
],
]);
}
} else {
if (qty > 0) {
storeCart([...carts, ...[{ merchantId, items: [{ productId, qty }] }]]);
}
}
};
return (
<CartsContext.Provider value={{ carts, addItem }}>
{children}
</CartsContext.Provider>
);
};
export default CartsContext;

How to change network in metamask using react js

I am developing my first Dapp, I am using metamask and web3 for this. As far now, I am able to get my wallet balance and connect account to metamask. Now I am trying switch between two networks, I am using handleChainChanged, also I am passing chainId and Networkversion but, it is giving me error. I am uncertain about returning anything from changeNetwork function or I only have to pass chainId and Networkversion.
import { useStoreApi } from "./storeApi";
import { useState } from "react";
import useWeb3 from "./useWeb3";
import { Button, TextField } from "#material-ui/core";
import "./App.css";
function App() {
const { balance, address, message, setAddress, setBalance } = useStoreApi();
const web3 = useWeb3();
// get user account on button click
const getUserAccount = async () => {
if (window.ethereum) {
try {
await window.ethereum.enable();
web3.eth.getAccounts().then((accounts) => {
setAddress(accounts[0]);
updateBalance(accounts[0]);
console.log(accounts);
});
} catch (error) {
console.error(error);
}
} else {
alert("Metamask extensions not detected!");
}
web3.eth.getChainId().then(console.log);
};
const updateBalance = async (fromAddress) => {
await web3.eth.getBalance(fromAddress).then((value) => {
setBalance(web3.utils.fromWei(value, "ether"));
});
};
const changeNetwork = async () => {
if (window.ethereum) {
try {
await window.ethereum.enable();
window.ethereum._handleChainChanged({
chainId: 0x1,
networkVersion: 1,
});
} catch (error) {
console.error(error);
}
}
};
return (
<div className="App">
<header className="App-header">
{address ? (
<>
<p> Balance: {balance} </p>
</>
) : null}
<Button
onClick={() => getUserAccount()}
variant="contained"
color="primary"
>
Connect your account
</Button>
<Button onClick={changeNetwork} variant="contained" color="primary">
Switch to mainnet ethereum
</Button>
</header>
</div>
);
}
export default App;
What if the user doesn't have the required network added? Here is an expanded version which tries to switch, otherwise add the network to MetaMask:
const chainId = 137 // Polygon Mainnet
if (window.ethereum.networkVersion !== chainId) {
try {
await window.ethereum.request({
method: 'wallet_switchEthereumChain',
params: [{ chainId: web3.utils.toHex(chainId) }]
});
} catch (err) {
// This error code indicates that the chain has not been added to MetaMask
if (err.code === 4902) {
await window.ethereum.request({
method: 'wallet_addEthereumChain',
params: [
{
chainName: 'Polygon Mainnet',
chainId: web3.utils.toHex(chainId),
nativeCurrency: { name: 'MATIC', decimals: 18, symbol: 'MATIC' },
rpcUrls: ['https://polygon-rpc.com/']
}
]
});
}
}
}
You can use wallet_switchEthereumChain method of RPC API of Metamask
Visit: https://docs.metamask.io/guide/rpc-api.html#wallet-switchethereumchain
const changeNetwork = async () => {
if (window.ethereum) {
try {
await window.ethereum.request({
method: 'wallet_switchEthereumChain',
params: [{ chainId: Web3.utils.toHex(chainId) }],
});
});
} catch (error) {
console.error(error);
}
}
changeNetwork()
export async function switchToNetwork({
library,
chainId,
}: SwitchNetworkArguments): Promise<null | void> {
if (!library?.provider?.request) {
return
}
const formattedChainId = hexStripZeros(
BigNumber.from(chainId).toHexString(),
)
try {
await library.provider.request({
method: 'wallet_switchEthereumChain',
params: [{ chainId: formattedChainId }],
})
} catch (error) {
// 4902 is the error code for attempting to switch to an unrecognized chainId
// eslint-disable-next-line #typescript-eslint/no-explicit-any
if ((error as any).code === 4902) {
const info = CHAIN_INFO[chainId]
await library.provider.request({
method: 'wallet_addEthereumChain',
params: [
{
chainId: formattedChainId,
chainName: info.label,
rpcUrls: [info.addNetworkInfo.rpcUrl],
nativeCurrency: info.addNetworkInfo.nativeCurrency,
blockExplorerUrls: [info.explorer],
},
],
})
// metamask (only known implementer) automatically switches after a network is added
// the second call is done here because that behavior is not a part of the spec and cannot be relied upon in the future
// metamask's behavior when switching to the current network is just to return null (a no-op)
try {
await library.provider.request({
method: 'wallet_switchEthereumChain',
params: [{ chainId: formattedChainId }],
})
} catch (error) {
console.debug(
'Added network but could not switch chains',
error,
)
}
} else {
throw error
}
}
}

[react native][ios][react-native-background-fetch] dont't work on ios

I've implemented react-native-background-fetch in my app, and it works well on Android, but not on ios - Xcode Version 11.4
Simulate Background Fetch works. I noticed also that Background Fetch triggers once on the first application start, but not anymore. On Android Background Fetch triggers every 15 min.
My configuration is:
react-native: 0.62.1
react-native-background-fetch: 2.8.0
Here is my code:
App.js
class App extends Component {
componentDidMount () {
...
newPostNotificationService.initNotifications()
...
newPostsNotificationSevice.js
initNotifications = async () => {
this.configureNotifications() // Configures PushNotification
BackgroundFetch.registerHeadlessTask(this.fetchNewPosts)
BackgroundFetch.configure({
minimumFetchInterval: 15,
stopOnTerminate: false,
startOnBoot: true,
enableHeadless: true
}, this.fetchNewPosts,
(error) => {
console.log(error, '[js] RNBackgroundFetch failed to start')
})
BackgroundFetch.status((status) => {
switch (status) {
case BackgroundFetch.STATUS_RESTRICTED:
console.log('BackgroundFetch restricted')
break
case BackgroundFetch.STATUS_DENIED:
console.log('BackgroundFetch denied')
break
case BackgroundFetch.STATUS_AVAILABLE:
console.log('BackgroundFetch is enabled')
break
}
})
}
fetchNewPosts Method in newPostsNotificationSevice.js
async fetchNewPosts(taskId) {
console.log('BackgroundFetch Start', taskId)
if (AppState.currentState === 'active') {
BackgroundFetch.finish(taskId)
return
}
dataStorageService.getSettings().then(async (settings) => {
if (!settings.notifications) {
BackgroundFetch.finish(taskId)
return
}
const url = config.API_URL + config.API_ENDPOINTS.POSTS + '&per_page=1&page=1'
let data
if (!config.DATA_MOCK.NEW_POSTS) {
data = await fetch(url)
.then((response) => {
return response.json()
})
.catch((error) => {
console.error(error)
})
} else {
data = postsMock.posts
}
data = data.map(item => {
return new PostShort(item)
})
data = data.filter(item => {
return item.sendPush
})
if (data.length === 0) {
BackgroundFetch.finish(taskId)
}
dataStorageService.getPostsIndex().then(postsIndex => {
if (postsIndex.length > 0 && postsIndex.indexOf(data[0].id) !== -1) {
BackgroundFetch.finish(taskId)
return
}
dataStorageService.getNotificationsIndex().then(notificationsIndex => {
if (notificationsIndex.indexOf(data[0].id) !== -1) {
BackgroundFetch.finish(taskId)
return
}
const notificationBody = entities.decode(data[0].title)
const notificationNumber = notificationsIndex.length + 1
console.log('notificationNumber', notificationNumber)
PushNotification.localNotification({
title: config.NOTIFICATION_TITLE, // (optional)
message: notificationBody, // (required)
playSound: true, // (optional) default: true
soundName: 'default',
number: notificationNumber,
id: notificationNumber.toString()
})
dataStorageService.insertIntoNotificationsIndex([data[0].id])
BackgroundFetch.finish(taskId)
})
})
})
}
I made iOS setup following this instructions: https://github.com/transistorsoft/react-native-background-fetch/blob/HEAD/docs/INSTALL-AUTO-IOS.md
Why Background Fetch doesn't trigger periodically on iOS like on Android does?

React Native Expo upload image from android not working

I am trying to upload images from my phone which is perfectly working in iOS but it failed to work in android, have tried several android devices and all could not upload image to server. Have tried all various methods that I could find in web but none is working, wondering is it a problem with my code, expo, RN or android?
React native Upload.js
import React from 'react';
import {
ActionSheetIOS,
Text,
View,
Platform,
Image,
TouchableOpacity
} from 'react-native';
import { ImagePicker } from 'expo';
import { ActionSheetProvider, connectActionSheet } from '#expo/react-native-action-sheet';
export default class TestUpload extends React.Component {
render() {
return(
<ActionSheetProvider>
<TestUploadApp />
</ActionSheetProvider>
);
}
}
#connectActionSheet
class TestUploadApp extends React.Component {
constructor(props) {
super(props)
this.state = {
imageSource: null,
imageUri: '',
imageName: '',
imageType: ''
}
}
async getPermissionAsync() {
const { CAMERA_ROLL, Permissions } = Expo;
try {
const status = await Permissions.askAsync(Permissions.CAMERA_ROLL);
return(status);
} catch(e) {
console.log(e);
}
if (status === 'granted') {
return CAMERA_ROLL.getCurrentPositionAsync({enableHighAccuracy: true});
} else {
throw new Error('Camera Roll permission not granted');
}
}
async getPermissionCamera() {
const { CAMERA, Permissions } = Expo;
try {
const status = await Permissions.askAsync(Permissions.CAMERA);
return(status);
} catch(e) {
console.log(e);
}
if (status === 'granted') {
return CAMERA.getCurrentPositionAsync({enableHighAccuracy: true});
} else {
throw new Error('Camera permission not granted');
}
}
componentDidMount() {
this.getPermissionAsync();
this.getPermissionCamera();
}
_PickImage = async() => {
let result = await ImagePicker.launchImageLibraryAsync({
allowsEditing: true,
aspect: [4, 4]
});
console.log(result);
if(!result.cancelled) {
let localUri = result.uri;
let filename = localUri.split('/').pop();
// Infer the type of the image
let match = /\.(\w+)$/.exec(filename);
let type = match ? `image/${match[1]}` : `image`;
let source = { uri: result.uri };
this.setState({
imageSource: source,
imageUri: localUri,
imageName: filename,
imageType: type
});
}
}
_PickCamera = async() => {
let result = await ImagePicker.launchCameraAsync({
allowsEditing: true,
aspect: [4, 4]
});
console.log(result);
if(!result.cancelled) {
let localUri = result.uri;
let filename = localUri.split('/').pop();
// Infer the type of the image
let match = /\.(\w+)$/.exec(filename);
let type = match ? `image/${match[1]}` : `image`;
let source = { uri: result.uri };
this.setState({
imageSource: source,
imageUri: localUri,
imageName: filename,
imageType: type
});
}
}
_ShowActionSheet = () => {
if(Platform.OS === 'ios') {
ActionSheetIOS.showActionSheetWithOptions({
options: ['Cancel', 'Take Photo', 'Choose From Gallery'],
cancelButtonIndex: 0,
},
(buttonIndex) => {
if(buttonIndex === 1) {
this._PickCamera();
} else if (buttonIndex === 2) {
this._PickImage();
}
});
} else if(Platform.OS === 'android') {
let options = ['Choose From Gallery', 'Take Photo', 'Cancel'];
let cancelButtonIndex = 2;
this.props.showActionSheetWithOptions({
options,
cancelButtonIndex,
},
(buttonIndex) => {
if(buttonIndex === 0) {
this._PickImage();
} else if (buttonIndex === 1) {
this._PickCamera();
}
});
}
}
SignUpProfile = () => {
const { imageUri, imageName, imageType } = this.state;
if(imageUri != null && imageUri != '') {
// Upload Image
let formData = new FormData();
formData.append('photo', {
uri: imageUri,
type: imageType,
name: imageName,
});
console.log(formData);
fetch(`${global.api}data_controller/signup_profile_upload_photo`, {
method: 'POST',
body: formData,
header: {
'Content-Type': 'multipart/form-data'
}
}).then((response) => response.json())
.then((responseJson) => {
console.log(responseJson);
}).catch((error) => {
console.error(error);
});
}
}
render() {
return(
<View>
<TouchableOpacity
onPress={this._ShowActionSheet}
>
<Image
style={{
width: 100,
height: 100,
}}
source={this.state.imageSource != null ? this.state.imageSource : require('../../assets/images/tempat_ibadah.jpg')}
/>
</TouchableOpacity>
<TouchableOpacity
onPress={ this.SignUpProfile }
>
<Text>Upload</Text>
</TouchableOpacity>
</View>
);
}
}
Backend PHP upload-photo.php
/* I am using CodeIgniter 3.1.5
* In this case I have tried several methods
*/
public function signup_profile_upload_photo() {
/* Method 1 using CI upload library */
/* Failed to upload in iOS and android */
$dir = './tmp';
if(!is_dir($dir)) {
if(mkdir($dir, 0777, TRUE)) {
$index = '<!DOCTYPE HTML><html><head><title>403 Forbidden</title></head><body><p>Directory access is forbidden.</p></body></html>';
write_file($dir . "/index.html", $index);
}
}
$config['upload_path'] = $dir;
$config['allowed_types'] = 'gif|jpg|png';
$this->load->library('upload', $config);
if($this->upload->do_upload('photo')) {
$callback['success'] = '1';
} else {
$callback['success'] = '0';
}
/* End of Method 1 */
/* Method 2, work perfectly in iOS, but not working in all android devices */
if(move_uploaded_file($_FILES['photo']['tmp_name'], './tmp/photo.jpg')) {
$callback['move'] = '1';
} else {
$callback['move'] = '0';
}
$data = json_encode($callback);
echo $data;
}
When I console.log my FormData in react native, the result are as follows:
/* Android */
/* When selecting image */
Object {
"cancelled": false,
"height": 3120,
"type": "image",
"uri": "file:///data/user/0/host.exp.exponent/cache/ExperienceData/%2540username%252Fprojectname/ImagePicker/538f376d-9a8c-4c4b-bc7f-bf3796785cec.jpg",
"width": 3120,
}
/* After appending to FormData
FormData {
"_parts": Array [
Array [
"photo",
Object {
"name": "538f376d-9a8c-4c4b-bc7f-bf3796785cec.jpg",
"type": "image/jpg",
"uri": "file:///data/user/0/host.exp.exponent/cache/ExperienceData/%2540username%252Fprojectname/ImagePicker/538f376d-9a8c-4c4b-bc7f-bf3796785cec.jpg",
},
],
],
}
/* iOS */
/* Selecting image */
Object {
"cancelled": false,
"height": 1125,
"type": "image",
"uri": "file:///var/mobile/Containers/Data/Application/200DE4DB-AD0D-40A2-9BF6-4C25B147B5B1/Library/Caches/ExponentExperienceData/%2540username%252Fprojectname/ImagePicker/1772B1F0-32EF-4212-8D56-374AD57535B9.png",
"width": 1122,
}
/* FormData */
FormData {
"_parts": Array [
Array [
"photo",
Object {
"name": "1772B1F0-32EF-4212-8D56-374AD57535B9.png",
"type": "image/png",
"uri": "file:///var/mobile/Containers/Data/Application/200DE4DB-AD0D-40A2-9BF6-4C25B147B5B1/Library/Caches/ExponentExperienceData/%2540username%252Fprojectname/ImagePicker/1772B1F0-32EF-4212-8D56-374AD57535B9.png",
},
],
],
}
When I tried to console.log $_FILES['photo']['tmp_name'] after uploading with iOS, it does return a value "C:/xampp/tmp/file", but when trying to console.log the file after uploading with android, it does not return any value.
Thank you all in advance

Categories