将EXIF数据写入使用DocumentFile类保存的图像 [英] Writing EXIF data to image saved with DocumentFile class

查看:125
本文介绍了将EXIF数据写入使用DocumentFile类保存的图像的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我需要以正确的方案从DocumentFile或Uri获取文件,而不是具有 content://com.android.externalstorage.documents/tree/primary的文件:设备的主存储器被选中。
要获取文件或图像的绝对路径,我需要一个带有file:/// storage / emulated / 0或storage / emulated / 0的文件,但我找不到一种方法来获取用于构建文件的正确Uri将图像写入EXIF数据。

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.

我的情况是:


  1. 用户选择一个保存图像的路径,该图像返回带有 content://com.android.externalstorage.documents onActivityResult()的Uri。我使用 treeUri.toString()将此路径保存到SharedPreferences,以备后用。

  2. 用户拍摄图片,并使用 DocumentFile.fromTreeUri(MainActivity.this,Uri.parse(uriString));

  3. 这在我失败的地方,得到了正确指向的文件到图像,带有content://的Uri不会返回现有图像。正确的Uri应该 file:/// storage / emulated / ,我可以使用以下命令将该Uri转换为文件 File filePath = new File(URI.create(saveDir.getUri()。toString()));

  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()));

如何获取从SAF UI获取的Uri或使用Uri构造文件所需的Uri?

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

编辑: ExifInterface支持库

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) {}
  }
}

注意: ExifInterface不适用于远程InputStream,例如从HttpURLConnection返回的远程InputStream。强烈建议仅将它们与content://或file:// URI一起使用。

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.

上面的代码段显然正在读取数据,因为它打开了InputStream来读取数据。我需要能够将EXIF数据写入JPEG文件。

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.

推荐答案

将Exif数据写入先前保存的图像的答案如果Api为24或以上,则使用 FileDescriptor 的已知内容Uri

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();
            }
        }
    }
}

如果Api低于24,必须在写入Exif数据后使用缓冲文件并将缓冲文件保存到 DocumentFile 的实际位置。

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;
    }

两种方法均可用于将Exif数据写入保存在设备内存中的图像或SD卡。您还可以使用Storage Access Framework中的有效Uri将图像保存到SD卡。

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.

我还找到了一种从内容Uri获取内存和SD卡的绝对路径的方法,但这与该问题无关,因此鼓励使用Uri而不是绝对路径,并且不导致未注意到的错误,我也无法使用绝对路径将图像保存到SD卡,只能从其中读取。

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.

这篇关于将EXIF数据写入使用DocumentFile类保存的图像的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆