Ionic 3 Read URL as base64 in Android - android

I'm using the Ionic 3 Camera and File plugins to work with image selection from the user's Android gallery app. The plugin returns a file uri which I want to use to extract a base64 encoded image with the File plugin, but I get a NOT_FOUND_ERR when I attempt to read the file from the cache directory. Here's a sample of my code and some log outputs:
this.camera.getPicture(this.getCameraOptionsForPhotoLibrary()).then((fileUri: string) => {
const fileNameIndex: number = fileUri.lastIndexOf('/') + 1;
// file:///storage/emulated/0/Android/data/com.myDomain.myApp/cache/
const cacheDirectoryFromFileUri = fileUri.substring(0, fileNameIndex );
// FB_IMG_1532921240445.jpg?1532982282636
const fileName = fileUri.substring(fileNameIndex);
this.file.readAsDataURL(cacheDirectoryFromFileUri, fileName).then(base64 => {
alert('base64 cacheDirectoryFromFileUri success: ' + JSON.stringify(base64));
}, err => {
alert('base64 cacheDirectoryFromFileUri err: ' + JSON.stringify(err));
});
Please let me know I can provide any more information to help troubleshoot.

While trying to replicate your error I did not get a NOT_FOUND_ERR.
Although, this code block
this.camera.getPicture(this.getCameraOptionsForPhotoLibrary()).then((fileUri: string) => {
const fileNameIndex: number = fileUri.lastIndexOf('/') + 1;
// file:///storage/emulated/0/Android/data/com.myDomain.myApp/cache/
const cacheDirectoryFromFileUri = fileUri.substring(0, fileNameIndex );
// FB_IMG_1532921240445.jpg?1532982282636
const fileName = fileUri.substring(fileNameIndex);
this.file.readAsDataURL(cacheDirectoryFromFileUri, fileName).then(base64 => {
alert('base64 cacheDirectoryFromFileUri success: ' + JSON.stringify(base64));
}, err => {
alert('base64 cacheDirectoryFromFileUri err: ' + JSON.stringify(err));
});
}
gave me this error
base64 cacheDirectoryFromFileUri err: {"code":5,"message":"ENCODING_ERR"}
I was using these Options
{
quality: 100,
destinationType: this.camera.DestinationType.FILE_URI,
encodingType: this.camera.EncodingType.JPEG,
mediaType: this.camera.MediaType.ALLMEDIA,
sourceType: this.camera.PictureSourceType.PHOTOLIBRARY
}
The following line
const cacheDirectoryFromFileUri = fileUri.substring(0, fileNameIndex );
Gives me something like this
/storage/emulated/0/WhatsApp/Media/WhatsApp Images/
Upon changing that line to the following
const cacheDirectoryFromFileUri = 'file://'+fileUri.substring(0, fileNameIndex );
Solved the issue.
Or if you are trying to get the image from your cache after taking the image from your camera and want to get the base64 encoded string, you can use the Camera.DestinationType : DATA_URL
destinationType: this.camera.DestinationType.DATA_URL

I was unable to replicate Patra's answer the way it was described, but I did manage to extract some useful information through testing since I first posted.
In some cases I found if I remove the trailing digits after the ? (FB_IMG_1532921240445.jpg?1532982282636), the result was successfully returned. But this was conditional on a couple of device/configuration scenarios. For instance, it fails on Oreo on a Pixel 2 if I release an APK. In fact, it fails without the error callback being invoked, so I can't even handle the failure. But for some reason if I build from the CLI with the -lc flags on the same device, it will return the base64 result as intended.
When testing a 7.0 and 7.1 emulated device, it works as intended regardless of the build configuration.
It's a frustrating experience that the behavior is markedly different between devices and configurations. I'm especially curious about the livereload discrepancy, if anyone has any insight on why the file plugin behavior changes based on this flag it would be great to know.
I'm still trying to solve this, although apart from cracking open the plugin I'm not sure where to go. I have experimented with the Base64 Native Plugin which seems to work initially, but it doesn't seem to be actively managed and there are a lot of unresolved issues according to the github page. I may end up using a combination of the File and Base64 plugins to get what I need for now.
/** Update **/
A member of our team found the specific answer to our problem here:
https://forum.ionicframework.com/t/file-readastext-never-resolves/85375/24
If this link ever breaks, here is what we did to fix it:
Place cordova.js after polyfills.js in index.html
What a horrific bug.

Related

Axios POST request gives a "Network Error" when adding image to FormData structure in React Native

I'm currently building a simple app in React Native 0.62.2 for Android. I've been having some trouble with axios 0.19.2 (or even the fetch API) when trying to upload images to my API (which is written in node.js/express). The POST request is formulated as follows:
// UserService.js
export const postNewUser = async (newUser) => {
try {
const photo = {
uri: newUser.avatar.uri,
type: 'image/jpg',
name: newUser.avatar.fileName,
};
const formData = new FormData();
Object.keys(newUser).forEach(key => formData.append(key, newUser[key]));
formData.append('avatar', photo);
const response = await api.post('/users', formData);
return response.data;
} catch (err) {
console.log('TRACE error posting user: ', err);
return;
}
}
Here, the property newUser.avatar.uri is set by means of an image picker library, namely #react-native-image-picker 1.6.1. It gives me a NetworkError whenever I append the photo variable into the FormData. Setting the URI manually with some random image from the web results in the same error. Debbuging it from the Browser, it prints out some sort of stack trace like this one:
TRACE error posting user: Error: Network Error
at createError (C:\Users\Dell\Documents\Projetos\SmartestVet\node_modules\axios\lib\core\createError.js:16)
at EventTarget.handleError (C:\Users\Dell\Documents\Projetos\SmartestVet\node_modules\axios\lib\adapters\xhr.js:83)
at EventTarget.dispatchEvent (C:\Users\Dell\Documents\Projetos\SmartestVet\node_modules\event-target-shim\dist\event-target-shim.js:818)
at EventTarget.setReadyState (C:\Users\Dell\Documents\Projetos\SmartestVet\node_modules\react-native\Libraries\Network\XMLHttpRequest.js:575)
at EventTarget.__didCompleteResponse (C:\Users\Dell\Documents\Projetos\SmartestVet\node_modules\react-native\Libraries\Network\XMLHttpRequest.js:389)
at C:\Users\Dell\Documents\Projetos\SmartestVet\node_modules\react-native\Libraries\Network\XMLHttpRequest.js:502
at RCTDeviceEventEmitter.emit (C:\Users\Dell\Documents\Projetos\SmartestVet\node_modules\react-native\Libraries\vendor\emitter\EventEmitter.js:189)
at MessageQueue.__callFunction (C:\Users\Dell\Documents\Projetos\SmartestVet\node_modules\react-native\Libraries\BatchedBridge\MessageQueue.js:425)
at C:\Users\Dell\Documents\Projetos\SmartestVet\node_modules\react-native\Libraries\BatchedBridge\MessageQueue.js:112
at MessageQueue.__guard (C:\Users\Dell\Documents\Projetos\SmartestVet\node_modules\react-native\Libraries\BatchedBridge\MessageQueue.js:373)
If I, for example, comment out the line formData.append('avatar', photo); it works perfectly, i.e., my API receives the request accordingly. So I think this might not be a CORS-related issue. Also, other requests, such as GETs and even other POSTs are working just fine.
I know there's a bunch of other related posts here in SO and also in GitHub, some of them related to the exact same issue. But none of the solutions I found worked out for me.
In case someone wants to check out how the routes in my API are implemented just hit me up and I will provide the code here.
Thanks in advance for any help you might give me!
I'm having the same issue, using the formData but without the file upload it works just fine. I did a lot of research and what I've found is an old issue that still's active in the react native repo. The solution that's suggested is using a library called rn-fetch-blob but I couln't implement it on my project. If you can make it work share your work around please.

expo + react-native: There was a problem sending log messages

I am building a react-native app that I recently moved to expo. The app seems to display the expected screen, but before it completes, I am receiving the following error message: console.error: "There was a problem sending log messages to your development environment, {"name": "Error"}". When I view the expo browser screen I see the following stack trace when I click on the device:
node_modules/expo/build/logs/LogSerialization.js:146:14 in _captureConsoleStackTrace
node_modules/expo/build/logs/LogSerialization.js:41:24 in Object.serializeLogDataAsync$
node_modules/regenerator-runtime/runtime.js:62:39 in tryCatch
node_modules/regenerator-runtime/runtime.js:288:21 in Generator.invoke [as _invoke]
node_modules/regenerator-runtime/runtime.js:114:20 in Generator.prototype.(anonymous
node_modules/regenerator-runtime/runtime.js:62:39 in tryCatch
node_modules/regenerator-runtime/runtime.js:152:19 in invoke
node_modules/regenerator-runtime/runtime.js:187:10 in <unknown>
node_modules/promise/setimmediate/core.js:45:4 in tryCallTwo
node_modules/promise/setimmediate/core.js:200:12 in doResolve
Here is a screenshot of the error:
What does this error mean? I found some doc referring to removing console.log statements and removed the ones I had but that did not help.
This is due to the fact that the React native console logger CANNOT parse the JSON object coming from Axios.
I can guarantee that anyone who is having this error is not PARSING the JSON object before logging it to the console.
CODE THAT WILL GIVE THIS ERROR:
Axios.post(URL).then(function (response)
{
console.log("POST RESPONSE: "+response);
}
CODE THAT FIXES THIS ERROR:
Axios.post(URL).then(function (response)
{
console.log("POST RESPONSE: "+JSON.stringify(response));
}
I ran into this weird error as well this morning. I am developing in react native with Expo Client for an app I'm building with the popular MERN stack (mongoDB, Express, React Native and Node.js). I am mentioning that because I use a lot, I mean A LOT of console logs in the backend and this didn't cause me any problem thus far. So in my case, I was not sure if this error originated from any console.log I was using.
I checked the expo debugger's stacktrace in the console (in port 19001), because the red screen doesn't provide much info on the origin of the error (a lot of <unknown> in place of functions) and I saw that it had something to do with my actions functions and the payload I was sending to my reducer when I performed a specific action that was communicating with the backend. The backend's response was formatted like this:
payload: {
config: {
.
.
.
}
data: { the only part that i needed... }
headers: {
.
.
.
}
..other stuff from the response..
There's not much to notice above, but this:
The actual paylaod I was interested in is under the prop key data and was the only thing I needed from the response. BUT, in my ignorance I was sending everything to my reducer. So what I am saying is that I was sending a really big object as payload and I only needed a part of it. So when I did some destructuring and kept the data that I mentioned above, the error went away.
In conclusion, for others that may stumble across this "error" which isn't actually an error, because the app doesn't crash or anything, since you can dismiss the window and the app goes on, when you do some fetching from the server, make sure you keep only the data and not the whole response object, along with the meta from the call. It seems that redux-logger throws this because it doesn't like the structure of it.
To simplify all the answers above, This issue only happens when you log an object which is too big for console to display. So when ever logging the response from an API or Server be sure to add JSON.stringify(result).
This resolved the issue for me.
I have also ran into this issue but due to other causes.
BACKGROUND:
Project stack (Just what is important to the error) :
expo: ^34.0.1
react-native: SDK 34
react-navigation: 4.0.5
react-navigation-drawer: ^2.2.1
In this project I was not using react-redux or axios I'm actually using graphql , #apollo/react-hooks and apollo-boost to take care of network requests and local state management.
ANSWER:
As you can see in the BACKGROUND, I am using react-navigation. I was creating a drawer navigator with createDrawerNavigator according to the React Navigation API
I wanted to use the contentComponent property of the DrawerNavigatorConfig to create custom DrawerNavigatorItems.
I put and anonymous arrow function in the contentComponent property with the only argument of props.
THIS CAUSED THE ERROR:
I placed a console.log() inside the anonymous arrow function I mentioned above
I received the error on my iOS simulator that read:
`console.error: "There was a problem sending log messages to your development environment"
See my code below:
import {createDrawerNavigator, DrawerNavigatorItems} from "react-navigation-drawer";
import ProfileNavigator from "../Profile/Profile";
import Colors from "../../constants/colors";
import {AsyncStorage, Button, SafeAreaView, View} from "react-native";
import React from "react";
import {Logout} from "../Common";
import HomeNavigator from "../Home/Home";
const AppDrawerNavigator = createDrawerNavigator(
{
Profile: ProfileNavigator
},
{
contentOptions: {
activeTintColor: Colors.primary
},
contentComponent: props => {
console.log(props) // THIS IS THE ISSUE CAUSING THE ERROR!!!!!!
return (
<View style={{ flex: 1, paddingTop: 20 }}>
<SafeAreaView forceInset={{ top: 'always', horizontal: 'never' }}>
<DrawerNavigatorItems {...props} />
<Button
title="Logout"
color={Colors.primary}
onPress={Logout}
/>
</SafeAreaView>
</View>
)
},
drawerType: 'slide',
unmountInactiveRoutes: true
}
)
export default AppDrawerNavigator
I got this error consistently when dumping the result of a fetch call to console like: console.log(result).
Once I used:
console.log(JSON.stringify(result))
the problem went away.
I had the same issue today and the fix was to add response.json()
fetch(endpoint, {
method: 'GET',
headers: {
Accept: 'application/json',
}
})
.then(response => response.json())
.then(response => {
console.log('response', response)
})
This is the problem that comes with console.log (In VSCode, Ctrl + Shift + F to search all then type console.log to find where it is).
Convert from
console.log(error.config);
to
console.log(JSON.stringify(error.config, null, 2));
(null, 2 is to keep the print pretty)
Objects being sent to console.log are causing the red screen. As others noted you need to stringify all objects.
For those who don't have time to update every console.log in their app, simply do a global replace of console.log with a new function name (we used global.ourLog) and use this code to look at every param and stringify if an object. This function was put in the first line of our App.js of our expo app.
// this routine corrects the red screen
// of death with expo logging
// by taking objects that are logged and ensuring
// they are converted to strings
global.ourLog = function() {
let s = ''
for ( let a of arguments ) {
if ( typeof a === 'string') {
s += a
} else if ( typeof a === 'number') {
s += a
} else {
s += JSON.stringify(a,null,2)
}
s += '\t'
}
console.log(s)
}
then use like any other console.log (or replace every console.log with that new function name).
global.ourLog( "a string", anObject, err)
Hope this helps someone, it saved us a ton of time.

React native android undefined is not a function works in IOS

Works in IOS and works in Android when the debugger is running, but doesn't work via Android Simulator. I get this message via react-native log-android and basically I am just having nothing returned to the screen:
12-02 10:39:58.511 22502 24204 W ReactNativeJS: TypeError: undefined is not a function (near '...}).flat()
Android Picture
IOS Picture
Here is the fetch function I am using:
import axios from 'axios';
export const getData = async url => {
try {
const response = await axios.get(url);
const data = response.data;
return data;
} catch (error) {
console.log(error);
}
};
export default getData;
Inside of my componentDidMount, where I call the endpoint using the GetData function above:
componentDidMount() {
const teamsAPI = 'https://statsapi.web.nhl.com/api/v1/teams';
getData(teamsAPI).then(teams => {
const teamData = teams.teams
.map(({ id, name }) => ({
teamId: id,
teamName: name
}))
.flat()
this.setState({
teams: teamData
});
});
}
Everything has since been moved to REDUX, but I looked back at one of my branches today with the more basic code shared above and had the issue back then with this code as well. Unfortunately didn't realize all the differences with code compilations till now. Understand that the issue is probably because of 2 compilers, but have no idea how to approach the issue/ why there would be a type error in one and not the other.
It works with debugger I think due to what was mentioned here:
React Native behavior different in simulator / on device / with or without Chrome debugging
Edit: wanted to mention I've already done a cache reset and deleted the build folder and rebuilt
I tried out your code and the promise rejecting is happing for me in both Android and iOS. It is being caused by the .flat() removing it stops the promise rejection from occurring.
Looking at the data that you are mapping there there doesn't seem to be a need to flatten the data as it comes back as a array of objects with no other arrays inside it.
Could removing the .flat() be a possible solution for you?
You can see here for more information about .flat() and how it is still experimental array.prototype.flat is undefined in nodejs
I would also consider returning something from your getData function when it makes an error or perhaps use a promise with it that way you can handle an error.

Checking disk space on cordova iOS

I am trying to check disk space available in mobile using below code:
cordova.exec(function(result) {
var diskSizeInMB = result/1024;
alert(diskSizeInMB)
}, function(error) {
alert("Error: " + error);
}, "File", "getFreeDiskSpace", []);
In Android device it gives me correct result, whereas if I use iPhone/iPad it always returns me 0 as a output. Can anyone guide me how to resolve this issue? Or is there a way to check the disk size in iOS using cordova without writing custom plugin? To make this happen I haven't changed any configuration changes
It was a bug, but was fixed long time ago.
Old answer:
I confirm there is a bug on the code, the problem is getFreeDiskSpace is undocumented and cordova team want to remove the native code as getFreeDiskSpace isn't part of the w3c file API.
If you want to fix the bug for you app, go to CDVFile.m and change the line
NSNumber* pNumAvail = [self checkFreeDiskSpace:self.appDocsPath];
to
NSNumber* pNumAvail = [self checkFreeDiskSpace:self.rootDocsPath];
on the getFreeDiskSpace method

Cordova 3.6: how can I extract GPS Exif data from photo library in Android?

I'm developing an app with Cordova 3.6.x with Angularjs. Everything seems to work fine except the fact I can't extract date and location from the exif datas of an image taken from the photo library.
My target device is Android 4+
Since now I tested:
- https://github.com/lorinbeer/cordova-exif-utility ( no android support :( )
- https://github.com/guilhermefarias/cordova-exif (returns errors such "processMessage failed: Error: TypeError: Cannot call method 'toString' of undefined" and "processMessage failed: Message: S01 File1189144150")
- https://github.com/kamilersz/cordova-plugin-exif (couldn't understand how it works)
On the net I read that Cordova strips all the exif datas before returning the image. Is it really impossible to get those datas?
Can you please help me figure out how to solve this issue?
UPDATE
I use Angular alongside ngCordova, ui-router and https://github.com/guilhermefarias/cordova-exif. So I'm taking advantage of the "resolve" feature of ui-router that has:
resolve: {
picture: ['$cordovaCamera', function($cordovaCamera) {
var options = {
quality : 75,
destinationType : navigator.camera.DestinationType.FILE_URI,
sourceType: Camera.PictureSourceType.PHOTOLIBRARY,
allowEdit : false,
correctOrientation: true,
encodingType: Camera.EncodingType.JPEG,
saveToPhotoAlbum: false
};
return $cordovaCamera.getPicture(options); //TODO: we need to handle errors
}]
}
Then inside the corresponding controller I have
myApp.controller('UploadController', [
'$scope',
'picture',
function( $scope, picture ) {
//Picture
$scope.snapShot = picture;
CordovaExif.readData(picture, function(exifObject) {
console.log(exifObject);
});
etc..
But I get the following error
processMessage failed: Error: TypeError: Cannot call method 'toString' of undefined
processMessage failed: Stack: TypeError: Cannot call method 'toString' of undefined
Followed by the stacktrace and an enormous string that I'm quite sure is base64 even if it doesn't work with any base64 to image decoder I tried online...
You are right about at least of the two plugins:
lorinbeer's Cordova Exif Utility has no Android support
kamilersz' cordova-plugin-exif only has API for setting some Exif meta data and for getting Wi-Fi information from device.
But the Cordova Exif by guilhermefarias seems really promising and if you show the code you were trying, I might be able to help you. And of course it isn't impossible to get the Exif as it is part of the actual file. It is just the Cordova's default camera plugin that loses the Exif when selecting image from gallery. This seems to be really well done (based on code I read) and documented.
Then there is also the possibility to just read the image as binary data and apply something like Exif.js to read the Exif. See this answer by user Richard Nichols on how to do this.
kamilersz/cordova-plugin-exif was meant only for getting Wi-fi information from device since I made that for a sole assignment. You should use Cordova Exif by guilhermefarias with broader API.
taken straight from the repo, for #Shree's pleasure
Complete Example
This example show how its simple get exif information of photo taken by a smartphone.
var options = {
quality: 90,
sourceType: 2,
destinationType: 1,
};
function onSuccess(imageURI) {
CordovaExif.readData(imageURI, function(exifObject) {
console.log(exifObject);
});
};
function onFail(message) {
console.log('Failed because: ' + message);
};
navigator.camera.getPicture(onSuccess, onFail, options);

Categories

Resources