Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Android crashed with OutOfMemoryError while select videos #2037

Open
krmao opened this issue Mar 25, 2024 · 6 comments
Open

Android crashed with OutOfMemoryError while select videos #2037

krmao opened this issue Mar 25, 2024 · 6 comments

Comments

@krmao
Copy link

krmao commented Mar 25, 2024

Version

Tell us which versions you are using:

  • react-native-image-crop-picker v0.40.3
  • react-native v0.72.4

Platform

Tell us to which platform this issue is related

  • Android

Expected behaviour

not crash

Actual behaviour

crash

Steps to reproduce

  1. select big video

  2. crash

Attachments

Sentry crash logs

OutOfMemoryError: Failed to allocate a 295420368 byte allocation with 6291456 free bytes and 250MB until OOM, target footprint 11852424, growth limit 268435456

?
58.34.154.234
ID:035070fa-1147-48fd-afc7-d2532ae4d6d9


Android
版本:13


V2283A
型号:V2283A


最新

OutOfMemoryError
Failed to allocate a 295420368 byte allocation with 6291456 free bytes and 250MB until OOM, target footprint 11852424, growth limit 268435456
mechanism
UncaughtExceptionHandler
handled
false

com.reactnative.ivpusic.imagepicker.PickerModule 位置 createExternalStoragePrivateFile 行 627
com.reactnative.ivpusic.imagepicker.PickerModule 位置 resolveRealPath 行 602
com.reactnative.ivpusic.imagepicker.PickerModule 位置 getAsyncSelection 行 496
com.reactnative.ivpusic.imagepicker.PickerModule 位置 imagePickerResult 行 783
com.reactnative.ivpusic.imagepicker.PickerModule 位置 onActivityResult 行 877
com.facebook.react.bridge.ReactContext 位置 onActivityResult 行 375
com.facebook.react.ReactInstanceManager 位置 onActivityResult 行 822
com.facebook.react.ReactDelegate 位置 onActivityResult 行 107
com.facebook.react.ReactActivityDelegate 位置 onActivityResult 行 136
com.facebook.react.ReactActivity 位置 onActivityResult 行 70
android.app.Activity 位置 dispatchActivityResult 行 9154
android.app.ActivityThread 位置 deliverResults 行 5814
android.app.ActivityThread 位置 handleSendResult 行 5860
android.app.servertransaction.ActivityResultItem 位置 execute 行 67
android.app.servertransaction.ActivityTransactionItem 位置 execute 行 45
android.app.servertransaction.TransactionExecutor 位置 executeCallbacks 行 135
android.app.servertransaction.TransactionExecutor 位置 execute 行 95
android.app.ActivityThread$H 位置 handleMessage 行 2627
android.os.Handler 位置 dispatchMessage 行 106
android.os.Looper 位置 loopOnce 行 223
android.os.Looper 位置 loop 行 324
android.app.ActivityThread 位置 main 行 8679
java.lang.reflect.Method 位置 invoke
com.android.internal.os.RuntimeInit$MethodAndArgsCaller 位置 run 行 582

here

    private File createExternalStoragePrivateFile(Context context, Uri uri) throws FileNotFoundException {
        InputStream inputStream = context.getContentResolver().openInputStream(uri);

        String extension = this.getExtension(context, uri);
        File file = new File(context.getExternalCacheDir(), "/temp/" + System.currentTimeMillis() + "." + extension);
        File parentFile = file.getParentFile();
        if (parentFile != null) {
            parentFile.mkdirs();
        }

        try {
            // Very simple code to copy a picture from the application's
            // resource into the external file.  Note that this code does
            // no error checking, and assumes the picture is small (does not
            // try to copy it in chunks).  Note that if external storage is
            // not currently mounted this will silently fail.
            OutputStream outputStream = new FileOutputStream(file);
            byte[] data = new byte[inputStream.available()];
            inputStream.read(data);
            outputStream.write(data);
            inputStream.close();
            outputStream.close();
        } catch (IOException e) {
            // Unable to create file, likely because external storage is
            // not currently mounted.
            Log.w("image-crop-picker", "Error writing " + file, e);
        }

        return file;
    }

the key is

        byte[] data = new byte[inputStream.available()];
@krmao krmao changed the title OutOfMemoryError Android crashed with OutOfMemoryError while select videos Mar 25, 2024
@krmao
Copy link
Author

krmao commented Mar 25, 2024

I will try with patch by chatgpt for a test

diff --git a/node_modules/react-native-image-crop-picker/android/src/main/java/com/reactnative/ivpusic/imagepicker/PickerModule.java b/node_modules/react-native-image-crop-picker/android/src/main/java/com/reactnative/ivpusic/imagepicker/PickerModule.java
index 5de0845..5042117 100644
--- a/node_modules/react-native-image-crop-picker/android/src/main/java/com/reactnative/ivpusic/imagepicker/PickerModule.java
+++ b/node_modules/react-native-image-crop-picker/android/src/main/java/com/reactnative/ivpusic/imagepicker/PickerModule.java
@@ -599,7 +599,8 @@ class PickerModule extends ReactContextBaseJavaModule implements ActivityEventLi
                     && !path.startsWith(externalFilesDirPath)
                     && !path.startsWith(cacheDirPath)
                     && !path.startsWith(FilesDirPath)) {
-                File copiedFile = this.createExternalStoragePrivateFile(activity, uri);
+                // File copiedFile = this.createExternalStoragePrivateFile(activity, uri);
+                File copiedFile = this.createExternalStoragePrivateFileV2(activity, uri);
                 path = RealPathUtil.getRealPathFromURI(activity, Uri.fromFile(copiedFile));
             }
         }
@@ -638,6 +639,50 @@ class PickerModule extends ReactContextBaseJavaModule implements ActivityEventLi
         return file;
     }
 
+    private File createExternalStoragePrivateFileV2(Context context, Uri uri) throws IOException {
+        InputStream inputStream = null;
+        OutputStream outputStream = null;
+        File file = null;
+        try {
+            inputStream = context.getContentResolver().openInputStream(uri);
+            if (inputStream == null) {
+                throw new FileNotFoundException("InputStream is null for URI: " + uri);
+            }
+            String extension = this.getExtension(context, uri);
+            file = new File(context.getExternalCacheDir(), "/temp/" + System.currentTimeMillis() + "." + extension);
+            File parentFile = file.getParentFile();
+            if (parentFile != null) {
+                parentFile.mkdirs();
+            }
+            outputStream = new FileOutputStream(file);
+            byte[] buffer = new byte[8192];
+            int bytesRead;
+            while ((bytesRead = inputStream.read(buffer)) != -1) {
+                outputStream.write(buffer, 0, bytesRead);
+            }
+        } catch (IOException e) {
+            Log.w("image-crop-picker", "Error writing " + file, e);
+            throw e;
+        } finally {
+            if (inputStream != null) {
+                try {
+                    inputStream.close();
+                } catch (IOException e) {
+                    Log.e("image-crop-picker", "Error closing InputStream", e);
+                }
+            }
+            if (outputStream != null) {
+                try {
+                    outputStream.close();
+                } catch (IOException e) {
+                    Log.e("image-crop-picker", "Error closing OutputStream", e);
+                }
+            }
+        }
+        return file;
+    }
+
+
     public String getExtension(Context context, Uri uri) {
         String extension;
 

and add the code to AndroidManifest.xml

        android:largeHeap="true"
        android:hardwareAccelerated="true"

@truongnguyenceres
Copy link

truongnguyenceres commented Mar 29, 2024

It worked!

@atultiwaree
Copy link

I will try with patch by chatgpt for a test

diff --git a/node_modules/react-native-image-crop-picker/android/src/main/java/com/reactnative/ivpusic/imagepicker/PickerModule.java b/node_modules/react-native-image-crop-picker/android/src/main/java/com/reactnative/ivpusic/imagepicker/PickerModule.java
index 5de0845..5042117 100644
--- a/node_modules/react-native-image-crop-picker/android/src/main/java/com/reactnative/ivpusic/imagepicker/PickerModule.java
+++ b/node_modules/react-native-image-crop-picker/android/src/main/java/com/reactnative/ivpusic/imagepicker/PickerModule.java
@@ -599,7 +599,8 @@ class PickerModule extends ReactContextBaseJavaModule implements ActivityEventLi
                     && !path.startsWith(externalFilesDirPath)
                     && !path.startsWith(cacheDirPath)
                     && !path.startsWith(FilesDirPath)) {
-                File copiedFile = this.createExternalStoragePrivateFile(activity, uri);
+                // File copiedFile = this.createExternalStoragePrivateFile(activity, uri);
+                File copiedFile = this.createExternalStoragePrivateFileV2(activity, uri);
                 path = RealPathUtil.getRealPathFromURI(activity, Uri.fromFile(copiedFile));
             }
         }
@@ -638,6 +639,50 @@ class PickerModule extends ReactContextBaseJavaModule implements ActivityEventLi
         return file;
     }
 
+    private File createExternalStoragePrivateFileV2(Context context, Uri uri) throws IOException {
+        InputStream inputStream = null;
+        OutputStream outputStream = null;
+        File file = null;
+        try {
+            inputStream = context.getContentResolver().openInputStream(uri);
+            if (inputStream == null) {
+                throw new FileNotFoundException("InputStream is null for URI: " + uri);
+            }
+            String extension = this.getExtension(context, uri);
+            file = new File(context.getExternalCacheDir(), "/temp/" + System.currentTimeMillis() + "." + extension);
+            File parentFile = file.getParentFile();
+            if (parentFile != null) {
+                parentFile.mkdirs();
+            }
+            outputStream = new FileOutputStream(file);
+            byte[] buffer = new byte[8192];
+            int bytesRead;
+            while ((bytesRead = inputStream.read(buffer)) != -1) {
+                outputStream.write(buffer, 0, bytesRead);
+            }
+        } catch (IOException e) {
+            Log.w("image-crop-picker", "Error writing " + file, e);
+            throw e;
+        } finally {
+            if (inputStream != null) {
+                try {
+                    inputStream.close();
+                } catch (IOException e) {
+                    Log.e("image-crop-picker", "Error closing InputStream", e);
+                }
+            }
+            if (outputStream != null) {
+                try {
+                    outputStream.close();
+                } catch (IOException e) {
+                    Log.e("image-crop-picker", "Error closing OutputStream", e);
+                }
+            }
+        }
+        return file;
+    }
+
+
     public String getExtension(Context context, Uri uri) {
         String extension;
 

and add the code to AndroidManifest.xml

        android:largeHeap="true"
        android:hardwareAccelerated="true"

@krmao I tried and it worked now I'm able to select media greater than 1GB but as i exceed 1.5 Gb I'm getting negative media size and app is getting freeze (App not responding)

@atultiwaree
Copy link

Is there anyway in android where I can get the size of media without reading the whole file

@krmao
Copy link
Author

krmao commented May 17, 2024

@atultiwaree

import { getImageMetaData, getVideoMetaData, Video } from 'react-native-compressor';


const getImageInfo = (filePath: string) => {
  return getImageMetaData(filePath).then(result => ({
    ...result,
    size: displayUtil.IS_IOS ? result.size : result.size * 1024, // android 下 size 发现少了 1024倍
  }));
};

const getVideoInfo = (filePath: string) => {
  return getVideoMetaData(filePath).then(result => ({
    ...result,
    size: displayUtil.IS_IOS ? result.size : result.size * 1024, // android 下 size 发现少了 1024倍
  }));
};

or

use RNFS to read file size

@atultiwaree
Copy link

getVideoMetaD

Thanks @krmao but to get the file path we have to use react-native-image-crop-picker and that's when the app is getting freeze how do I tackle that is there any native way of doing such things or any library I was using react-native-image-picker but it takes lot of time so I switched to RNICP

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants