React-native-image-crop-picker: openCropper, error [permission denied]

Created on 14 Mar 2019  路  20Comments  路  Source: ivpusic/react-native-image-crop-picker

Version

Tell us which versions you are using:

  • react-native-image-crop-picker v0.23.0
  • react-native v0.58.3

Platform

Tell us to which platform this issue is related

  • Android

Expected behaviour

I hope to work like this...

  1. User have to agree with WRITE_EXTERNAL_PERMISSION (<---in my code, not image-crop-picker)
  2. User select any image in my flatlist
  3. openCropper work
  4. return, cropped image info

Actual behaviour

1, 2, 3 is OK. very good.
But, If i selected any image and then press the ok button,
return error ...

Error: /storage/emulated/0/Pictures/new_filename.jpg (Permission denied)

Attachments

Possible Unhandled Promise Rejection (id: 0):
Error: /storage/emulated/0/Pictures/9fc670cf-738e-4c61-8a57-ebaf44a1d0af.jpg (Permission denied)
createErrorFromErrorData@http://localhost:8081/index.delta?platform=android&dev=true&minify=false:2012:26
http://localhost:8081/index.delta?platform=android&dev=true&minify=false:1964:51
__invokeCallback@http://localhost:8081/index.delta?platform=android&dev=true&minify=false:2531:23
http://localhost:8081/index.delta?platform=android&dev=true&minify=false:2262:34
__guard@http://localhost:8081/index.delta?platform=android&dev=true&minify=false:2435:15
invokeCallbackAndReturnFlushedQueue@http://localhost:8081/index.delta?platform=android&dev=true&minify=false:2261:21
invokeCallbackAndReturnFlushedQueue@[native code]
ReactInstanceManager.detachViewFromInstance()

Love react-native-image-crop-picker? Please consider supporting our collective:
馃憠 https://opencollective.com/react-native-image-crop-picker/donate

Most helpful comment

Hi,

the error happens in Android Q regardless of "storage" permission.

The workaround is setting requestLegacyExternalStorage to true in Androidmanifest.xml
<application android:requestLegacyExternalStorage="true" ... >

All 20 comments

Any updates about it? I have the same issue with react-native v0.60.4 and react-native-image-crop-picker v0.25.0

First of all, thanks guys for this library :)

@Obi1Kennoby
@jaekwangLee

We have the same error in RN 0.60.4 with v0.25.0:
'/storage/emulated/0/Pictures/8ec90c24-e914-4b1e-b787-b27425e1fca1.jpg (Permission denied)'

Here is the code, it exit thru the CATCH sentence:

  ImagePicker.openCropper({
                    path: data.uri,
                    width: 1000,
                    height: 1000
                  }).then(image => {
                    console.log(image);

                    data.uri = image.path;
                    console.log('uri de foto: '+ data.uri);


                    this.setState({
                      url: data.uri
                    });

                    uri = data.uri;

                  fileName2 = uri.replace(/^.*[\\\/]/, '');


                  console.log('filename2 es: ' + fileName2);
                  envio = {name: fileName2, url: uri, type: this.props.phototype, sent: 'false', size: '2222', width: this.width, height: this.height } 

                  console.log('phototype :'+this.props.phototype)


                  this.props.sendActualMedia(envio);
                  console.log("Fin de espera larga ANDROID")
                  this.goBack();

                  if ( Platform.OS === 'ios')
                  timer = 1000;
                    else timer = 500;

                  setTimeout(() => {
                    console.log("hago esperar 1200ms para q siempre se abra el modal en qsoScreen");

                      this.props.openModalConfirmPhoto(320);
                  }, timer);



                  console.log('este debe aparecer primero');


                  }).catch((err) => {
                    console.log("cropImage Error", err.message);
                    this.setState({showCamera: true});
                    this.setState({buttonStatus: false});
                });

@Obi1Kennoby
@jaekwangLee

Guys, We solved the problem asking for Storage Permission before with _react-native-permissions_ library:

 Permissions.request('storage').then(res => {


          });

I don't know if it is becasue the new version of the library or the new version of React Native or the Android 8.1 ... just don't know, We couldn't go deep on this, but this issue was solved on Android for us. No more permission problem and the library is working fine again :)

Hope this helps.

I have solved the problem with really similar approach as @matamicen did. Just didn't use third-party dependency.

const granted = await PermissionsAndroid.check(PermissionsAndroid.PERMISSIONS.WRITE_EXTERNAL_STORAGE)

if (granted) {
// ...
}

Anyway I think this issue should be reopened as it's just a workaround, the problem is still in the library

After continue investigation I found the problem is in android native code side. To put it simply - openCropper does not grant WRITE_EXTERNAL_STORAGE permission as openPicker or openCamera do. That's why if you call for example openCamera and after it call openCropper it will work fine, because permissions already granted by openCamera API.
I created a PR with a fix

Hi,

the error happens in Android Q regardless of "storage" permission.

The workaround is setting requestLegacyExternalStorage to true in Androidmanifest.xml
<application android:requestLegacyExternalStorage="true" ... >

Hi,

the error happens in Android Q regardless of "storage" permission.

The workaround is setting requestLegacyExternalStorage to true in Androidmanifest.xml
<application android:requestLegacyExternalStorage="true" ... >

thanks my friend it work for me

Hey I am getting this error

Got error in cropping Error: Unknown error
    at Object.promiseMethodWrapper [as openCropper] (NativeModules.js:103)
    at _callee7$ (VM5 ImageUploadScreen.bundle:555)
    at tryCatch (runtime.js:45)
    at Generator.invoke [as _invoke] (runtime.js:274)
    at Generator.prototype.<computed> [as next] (runtime.js:97)
    at tryCatch (runtime.js:45)
    at invoke (runtime.js:135)
    at runtime.js:145
    at tryCallOne (core.js:37)
    at core.js:123

I am first requesting permission for

const granted = await PermissionsAndroid.request(
      PermissionsAndroid.PERMISSIONS.WRITE_EXTERNAL_STORAGE,
      {
        title: 'Share image',
        message: 'This app would like read media from storage.',
        buttonPositive: 'Please accept bare mortal',
      },
    ); 

and then calling ImagePicker.openCropper but it is not opening.
Any suggestions please?

@hackrx , you should add WRITE_EXTERNAL_STORAGE permission to android/app/src/main/AndroidManifest.xml
Screenshot 2020-10-13 at 12 32 39
and not request permission by your self as the module will popup the message for you

Also for Android 10 you could chack this

@ktomi42 Thanks for the quick reply but I have already added this in AndroidManifest.xml
image

I just want to ensure if the app has permission. And I am getting this output

image

Also for Android 10 you could chack this

I tried adding android:requestLegacyExternalStorage="true" but then I got an error in build

FAILURE: Build failed with an exception.

* What went wrong:
Execution failed for task ':app:processDebugResources'.
> A failure occurred while executing com.android.build.gradle.internal.tasks.Workers$ActionFacade
   > Android resource linking failed
     /media/hackrx/Extreme SSD/ReactProject/MyApp/CompanyName/testapp/android/app/src/main/AndroidManifest.xml:16:5-130:19: AAPT: error: attribute android:requestLegacyExternalStorage not found.

Hey @ktomi42 I solved this issue, actually I was setting unnecessary arguments to it, which was causing an error

await ImagePicker.openCropper({
      // path: this.state.filePath,
      path: this.state.filePath,
      width: widthOfImage,
      height: heightOfImage,
      // cropperTintColor: PrimaryColor,
      // // cropping: true,
      // cropperStatusBarColor: BlackColor,
      // freeStyleCropEnabled: true,
      // waitAnimationEnd: true,      //<-------------------
      // includeBase64: false,
      // // loadingLabelText: 'Loading please wait',
      // mediaType: 'photo',
      // cropperActiveWidgetColor: PrimaryColor,
      // cropperToolbarTitle: 'Crop image',
      // cropperToolbarColor: WhiteColor,
      // cropperToolbarWidgetColor: 'blue',
    }).then((image) => {
        console.log('Got cropped image ', image);

      })
      .catch((err) => {
        console.log('Got error in cropping', err);
      });

But now it is not returning the cropped image. #1435

This is what i put if i would like to crop an image:

const opts = {
  mediaType: 'photo',
  multiple: false,
  cropping: true,
  width: THUMBNAIL_SIZE,
  height: THUMBNAIL_SIZE,
  compressImageQuality: 0.4,
  compressImageMaxWidth: 800,
  compressImageMaxHeight: 800,
  // IOS only
  forceJpg: true
};

const res = await ImagePicker.openCamera(opts);

This is what i put if i would like to crop an image:

const opts = {
  mediaType: 'photo',
  multiple: false,
  cropping: true,
  width: THUMBNAIL_SIZE,
  height: THUMBNAIL_SIZE,
  compressImageQuality: 0.4,
  compressImageMaxWidth: 800,
  compressImageMaxHeight: 800,
  // IOS only
  forceJpg: true
};

const res = await ImagePicker.openCamera(opts);

But what if you have an imageURL like file:///storage/emulated/0/WhatsApp/Media/WhatsApp Images/IMG-20201012-WA0003.jpg and you want to crop this image? , can you please check if cropping is working for you? I have also reported a bug #1435 .

response from iPhone

    {
  "exif": null,
  "filename": null,
  "path": "/Users/kristijan/Library/Developer/CoreSimulator/Devices/34681E7C-2C4A-40E6-B07D-D0EE92A30D83/data/Containers/Data/Application/937A89B4-3C78-4FA1-95A4-639CFD40D808/tmp/react-native-image-crop-picker/E3FDBF78-A9CB-4028-BF3C-3C7C07D9BEFF.jpg",
  "height": 399,
  "width": 300,
  "data": null,
  "modificationDate": null,
  "localIdentifier": null,
  "size": 27616,
  "sourceURL": null,
  "mime": "image/jpeg",
  "cropRect": {
    "width": 3119,
    "height": 4152,
    "x": 105,
    "y": 0
  },
  "duration": null,
  "creationDate": null
}

response from iPhone

    {
  "exif": null,
  "filename": null,
  "path": "/Users/kristijan/Library/Developer/CoreSimulator/Devices/34681E7C-2C4A-40E6-B07D-D0EE92A30D83/data/Containers/Data/Application/937A89B4-3C78-4FA1-95A4-639CFD40D808/tmp/react-native-image-crop-picker/E3FDBF78-A9CB-4028-BF3C-3C7C07D9BEFF.jpg",
  "height": 399,
  "width": 300,
  "data": null,
  "modificationDate": null,
  "localIdentifier": null,
  "size": 27616,
  "sourceURL": null,
  "mime": "image/jpeg",
  "cropRect": {
    "width": 3119,
    "height": 4152,
    "x": 105,
    "y": 0
  },
  "duration": null,
  "creationDate": null
}

I am experiencing this bug on android haven't tested on iPhone (Since I am a Linux user).
Are you developing for android also? If you don't mind can you confirm this on android also, please?

response from Android

    {
  "cropRect": {
    "y": 0,
    "height": 600,
    "width": 450,
    "x": 175
  },
  "modificationDate": "1602595313000",
  "width": 300,
  "size": 98810,
  "mime": "image/jpeg",
  "height": 400,
  "path": "file:///storage/emulated/0/Android/data/com.domain/files/Pictures/372fceed-1149-4a75-85d3-fa950afcad7d.jpg"
}

response from Android

    {
  "cropRect": {
    "y": 0,
    "height": 600,
    "width": 450,
    "x": 175
  },
  "modificationDate": "1602595313000",
  "width": 300,
  "size": 98810,
  "mime": "image/jpeg",
  "height": 400,
  "path": "file:///storage/emulated/0/Android/data/com.domain/files/Pictures/372fceed-1149-4a75-85d3-fa950afcad7d.jpg"
}

@ktomi42 you just gave me a hope, can share a little code of your file? like how you are calling this (ImagePicker.openCropper()), are you using functional or class compoenent, are u calling this in componentDidMount (if you are using class component) , ... I am just curious about this.
Here is my code

ImageUpload.js

```/* eslint-disable react-native/no-inline-styles */
import ImagePicker from 'react-native-image-crop-picker';
import ImageResizer from 'react-native-image-resizer';
class NewPostUpload extends PureComponent {
constructor(props) {
super(props);
this.state = {
filePath: this.props.filePath
? this.props.filePath
: this.props.sharedImages
? this.props.sharedImages.value
: null,
defaultUri: DefaultAdd,
appState: AppState.currentState,
};
this.mount = false;
}

cropper = () => {
return {
width: 1000,
height: 800,
includeBase64: false,
compressImageQuality: 1,
cropperActiveWidgetColor: PrimaryColor,
cropperStatusBarColor: PrimaryColor,
};
};

//when app received image from another app
getSharedImage = () => {
ImageResizer.createResizedImage(this.state.filePath, 1000, 800, 'JPEG', 100)
.then((response) => {
// console.log(response);
if (this.mount) {
this.setState({
filePath: response.uri,
});
}
console.log('Image got resized', response);
this.props.setImageInfo(response.uri);
})
.catch((err) => {
// Oops, something went wrong. Check that the filename is correct and
// inspect err to get more details.
console.log('Error in Image resize', err);
this.setState({
filePath: null,
});
Alert.alert('Warning', 'Something went wrong');
});

cropReceivedImage = async () => { // <------------------this is the function to call openCropper()
let widthOfImage = 300;
let heightOfImage = 400;

console.log('Ca;ling cropper for path', this.state.filePath);
await ImagePicker.openCropper({
  path: this.state.filePath,
  width: widthOfImage,
  height: heightOfImage,
})
  .then((image) => {
    console.log('Got cropped image ', image);   // <-------------------------------It is not getting called
    // also set this filePath to redux
    this.props.setImageInfo(this.props.sharedImages.value);
  })
  .catch((err) => {
    console.log('Got error in cropping', err);
  });

};

checkForSharedImage = () => {
console.log('Check for image called');
if (
this.props.route.params.sharedImageNavigation &&
this.props.sharedImages &&
this.props.sharedImages .value
) {
console.log(
'we have shared image',
this.props.sharedImages .value,
);
this.props.setImageInfo(this.props.sharedImages.value);
console.log('Going to call cropReceivedImage', this.mount);
if (this.mount) {
this.setState(
{
filePath: this.props.sharedImages .value,
},
() => {
// Call a function to call cropper

        console.log('Calling crop rec after setting state');
        this.cropReceivedImage();
      },
    );
  }

  // also set this filePath to redux
  this.props.clearSharedImageArray();
} else {
  console.log('We have nothing', this.props.route.params);
}

};
componentDidMount() {

this.checkForSharedImage();
console.log(
  ' Component did mount run with props of image in redux ',
  this.props.filePath,
  this.state,
);
if (
  this.props.filePath ||
  (this.props.sharedImages  &&
    this.props.sharedImages.value)
) {

   this.cropReceivedImage();    // <----------------------------------- first call from here

}
this.mount = true;

}
componentWillUnmount() {
console.log('Image upload unmounted');
this.props.clearSharedImageArray();
}

modalVisibleHandler = () => {
if (this.mount) {
this.setState((preState) => ({
modalVisible: !preState.modalVisible,
}));
}
};
render() {

return (
   //.. <>
);

}
}
const mapStateToProps = (state) => {
return {
isUploading: state.image.isUploading,
sharedImages : state.image.sharedImages, // <-------------this var from reux contains the raw image URL that is to be cropped
};
};
export default connect(mapStateToProps, mapDispatchToProps)(NewPostUpload);

const styles = StyleSheet.create({
....styles,
})

```

I did nothing but copied the example from the readme.
I would suggest installing react-native-fs and before you call the cropper check if the file exists on the path you provide.

Cheers

Hi,

the error happens in Android Q regardless of "storage" permission.

The workaround is setting requestLegacyExternalStorage to true in Androidmanifest.xml
<application android:requestLegacyExternalStorage="true" ... >

Thanks, it works for me )

Was this page helpful?
0 / 5 - 0 ratings

Related issues

DISKONEKTeD picture DISKONEKTeD  路  3Comments

equesteo picture equesteo  路  3Comments

JodiWarren picture JodiWarren  路  3Comments

cwRichardKim picture cwRichardKim  路  3Comments

leelandclay picture leelandclay  路  3Comments