Writing EXIF data to image saved with DocumentFile class

与世无争的帅哥 提交于 2019-12-08 01:52:58

问题


I need to get a File from DocumentFile or Uri with correct scheme not the one with content://com.android.externalstorage.documents/tree/primary: if the device's main memory is selected. To get File or absolute path of the image i need the one with file:///storage/emulated/0 or storage/emulated/0 but i could not find a way to get correct Uri for building a File to write EXIF data to image.

My scenario is:

  1. User chooses a path to save images which returns Uri with content://com.android.externalstorage.documents onActivityResult(). I save this path with treeUri.toString() to SharedPreferences for using later.
  2. User takes a picture and image is saved with DocumentFile.fromTreeUri(MainActivity.this, Uri.parse(uriString));
  3. This where i fail, getting a File that correctly points to image, Uri with content:// does not return the existing image.Correct Uri should file:///storage/emulated/ and i can convert this Uri to file using File filePath = new File(URI.create(saveDir.getUri().toString()));

How can i get the Uri needed for consturcting File or File using Uri i got from SAF UI?

EDIT: ExifInterface Support Library is introduced for Android 7.1+ that can use InputStream or FileDescriptor.

Uri uri; // the URI you've received from the other app
InputStream in;
try {
  in = getContentResolver().openInputStream(uri);
  ExifInterface exifInterface = new ExifInterface(in);
  // Now you can extract any Exif tag you want
  // Assuming the image is a JPEG or supported raw format
} catch (IOException e) {
  // Handle any errors
} finally {
  if (in != null) {
    try {
      in.close();
    } catch (IOException ignored) {}
  }
}

Note: ExifInterface will not work with remote InputStreams, such as those returned from a HttpURLConnection. It is strongly recommended to only use them with content:// or file:// URIs.

Snippet above is reading data obviously since it opens an InputStream to read data. I need to be able to write EXIF data to JPEG files.


回答1:


Answer for writing Exif data to an image previously saved and with known content Uri using FileDescriptor if Api is 24 or above

private void writeEXIFWithFileDescriptor(Uri uri) {

    if (Build.VERSION.SDK_INT < 24) {
        showToast("writeEXIFWithInputStream() API LOWER 24", Toast.LENGTH_SHORT);
        return;
    }

    ParcelFileDescriptor parcelFileDescriptor = null;
    try {

        parcelFileDescriptor = mContext.getContentResolver().openFileDescriptor(uri, "rw");
        FileDescriptor fileDescriptor = parcelFileDescriptor.getFileDescriptor();
        showToast("writeEXIFWithFileDescriptor(): " + fileDescriptor.toString(), Toast.LENGTH_LONG);
        ExifInterface exifInterface = new ExifInterface(fileDescriptor);
        // TODO Create  Exif Tags class to save Exif data
        exifInterface.saveAttributes();

    } catch (FileNotFoundException e) {
        showToast("File Not Found " + e.getMessage(), Toast.LENGTH_LONG);

    } catch (IOException e) {
        // Handle any errors
        e.printStackTrace();
        showToast("IOEXception " + e.getMessage(), Toast.LENGTH_LONG);
    } finally {
        if (parcelFileDescriptor != null) {
            try {
                parcelFileDescriptor.close();
            } catch (IOException ignored) {
                ignored.printStackTrace();
            }
        }
    }
}

If Api is lower than 24 it's necessary to use a buffer file and save that buffer file to actual location with DocumentFile after writing Exif data is finished.

private boolean exportImageWithEXIF(Bitmap bitmap, DocumentFile documentFile) {
        OutputStream outputStream = null;
        File bufFile = new File(Environment.getExternalStorageDirectory(), "buffer.jpg");
        long freeSpace = Environment.getExternalStorageDirectory().getFreeSpace() / 1048576;
        double bitmapSize = bitmap.getAllocationByteCount() / 1048576d;

        showToast("exportImageWithEXIF() freeSpace " + freeSpace, Toast.LENGTH_LONG);
        showToast("exportImageWithEXIF() bitmap size " + bitmapSize, Toast.LENGTH_LONG);
        try {
            outputStream = new FileOutputStream(bufFile);
            // Compress image from bitmap with JPEG extension
            if (mCameraSettings.getImageFormat().equals(Constants.IMAGE_FORMAT_JPEG)) {
                isImageSaved = bitmap.compress(CompressFormat.JPEG, mCameraSettings.getImageQuality(), outputStream);
                showToast("isImageSaved: " + isImageSaved, Toast.LENGTH_SHORT);
            }

            if (isImageSaved) {
                writeEXIFWithFile(bufFile);
            }

        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } finally {
            if (outputStream != null) {
                try {
                    outputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        OutputStream os = null;
        InputStream is = null;
        try {
            int len;
            byte[] buf = new byte[4096];

            os = mContext.getContentResolver().openOutputStream(documentFile.getUri());
            is = new FileInputStream(bufFile);

            while ((len = is.read(buf)) > 0) {
                os.write(buf, 0, len);
            }

            os.close();
            is.close();

            if (bufFile != null) {
                bufFile.delete();
                bufFile = null;
            }

        } catch (IOException e) {
            e.printStackTrace();
        }

        return isImageSaved;
    }

Both methods can be used to write Exif data to image saved to device's memory or SD card. You can also save image to SD card using valid Uri from Storage Access Framework.

I also found a way to get absolute path for memory and SD card from content Uri but it's irrelevant for this question and using Uri instead of absolute path is encouraged and does not lead to unnoticed errors, i also wasn't able to save image to SD card with absolute path, only able to read from it.



来源:https://stackoverflow.com/questions/46442700/writing-exif-data-to-image-saved-with-documentfile-class

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!