react-native-image-resizer icon indicating copy to clipboard operation
react-native-image-resizer copied to clipboard

keepMeta isnot working in RN 0.61 ?

Open plakak13 opened this issue 5 years ago • 4 comments

Hi, after I update react native from 0.55 to 0.61 Exif inside image is removed. How to fix this? Thx

plakak13 avatar May 13 '20 03:05 plakak13

Working fine for me with RN 0.62.2 and react-native-image-resizer 1.4.5. Can you double check your source image metadata?

cristianoccazinsp avatar Jul 07 '21 16:07 cristianoccazinsp

I ran into this issue my self and managed to find the bug for android. Looking at the java code, the resizer first does the re-size operation and once that is finishes that if told to do so it will copy the exif from the src to the dest. While doing the resize, the code looks at the type of uri it has been given and process it accordingly. However, on the copyExif the type of uri is ignored and its being processed as file path. The code its wrapped with a try catch that will output to the android Logcat
ImageResizer::copyExif", "EXIF read failed"

But it ignores the problem all together.

Now for the fix, for a quick fix all the needed code is in place. A better fix requires rearranging most of the code so exif and resize operation are together. Since Exif can work on the input stream and the bitmap for resize is created from a stream which has been downloaded doing exif as part of the resize function would be more efficient and less error prone to errors like this one.

Quick fix here: (Notes: Ugly, only fixes the problem I had which is a content:// type uri. (when is local)) Http uri, not fixed but you can find the code in the file.

/**
 * Attempts to copy exif info from one file to another. Note: orientation, width, and height
    exif attributes are not copied since those are lost after image rotation.

 * imageUri: original image URI as provided from JS
 * dstPath: final image output path
 * Returns true if copy was successful, false otherwise.
*/
public static boolean copyExif(Context context, Uri imageUri, String dstPath){
    ExifInterface src = null;
    ExifInterface dst = null;
    String imageUriScheme = imageUri.getScheme();
    InputStream input = null;

    Log.d("copyExif","called uri schama " + imageUriScheme);
    try {
        // first lets see what type of uri we got
        if (imageUriScheme == null || !imageUriScheme.equalsIgnoreCase(SCHEME_CONTENT)) {
            try {
                File file = getFileFromUri(context, imageUri);
                if (!file.exists()) {
                    Log.d("copyExif","Source not found");
                    return false;
                }
                src = new ExifInterface(file.getAbsolutePath());
            } catch (Exception ignored) {
                Log.e("ImageResizer::copyExif", "EXIF read failed", ignored);
            }
        } else {
            // i guess is a content type 
            Log.e("ImageResizer::copyExif", "trying content uri type");
            ContentResolver cr = context.getContentResolver();
            try {
                input = cr.openInputStream(imageUri);
                if (input != null) {
                    src = new ExifInterface(input);
                    Log.d("ImageResizer::copyExif", "exif "+ src);
                } else {
                    Log.e("ImageResizer::copyExif", "EXIF read failed bitfactory did not return a stream");
                }
            } catch (Exception e) {
                Log.d("ImageResizer::copyExif", "Error while opening ", e);
            }
        }

        if(src == null) {
          return false;
        }

        try {
            dst = new ExifInterface(dstPath);
        } catch (Exception ignored) {
            Log.e("ImageResizer::copyExif", "EXIF read failed", ignored);
        }

        if(src == null || dst == null){
            return false;
        }

        try{
            for (String attr : EXIF_TO_COPY_ROTATED)
            {
                String value = src.getAttribute(attr);
                Log.e("ImageResizer::copyExif", "attr "+ attr+ " value " + value);
                if (value != null){
                    dst.setAttribute(attr, value);
                }
            }
            dst.saveAttributes();

        } catch (Exception ignored) {
            Log.e("ImageResizer::copyExif", "EXIF copy failed", ignored);
            return false;
        }
        return true;
    } finally {
        if(input!=null) {
          try {
            input.close();
          } catch (Exception e ) {
              Log.d("ImageResizer::copyExif", "Error while closing", e);
          }
        }
    }
}

dcssi avatar Oct 06 '21 17:10 dcssi

A PR would be awesome!

So the issue only happens on Android, and if a base64/uri is given instead of an actual file path?

Also, I think the code was initially thought to work with files, not base64 uris, so copyExif shouldn't even be called if a file is not provided. One could say it's a programmers error to use the keepMeta flag alongside a non-file uri. From the docs: This can only be true for JPEG images which are loaded from the file system (not Web).

cristianoccazinsp avatar Oct 06 '21 17:10 cristianoccazinsp

Yes on the case of the image being passed as base64 my fix would not help, however the code to fix that case is in the file. Look at the function loadBitmapFromBase64 (explains how to read and decode the base 64 image) Now if you take the variable decodedString and wrap it in a ByteArrayInputStream, you can pass the input stream to ExifInterface constructor.

dcssi avatar Oct 06 '21 17:10 dcssi

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

stale[bot] avatar Sep 01 '22 18:09 stale[bot]

This issue has been automatically closed. Thank you for your contributions.

stale[bot] avatar Sep 08 '22 18:09 stale[bot]