Android 将EXIF数据写入使用DocumentFile类保存的图像

Android 将EXIF数据写入使用DocumentFile类保存的图像,android,storage-access-framework,documentfile,android-storage,Android,Storage Access Framework,Documentfile,Android Storage,我需要从DocumentFile或Uri中获取一个具有正确方案的文件,而不是具有content://com.android.externalstorage.documents/tree/primary:如果选择了设备的主存储器。 要获取图像的文件或绝对路径,我需要一个file:///storage/emulated/0 或storage/emulated/0,但我找不到一种方法来获取正确的Uri,以便构建一个将EXIF数据写入映像的文件 我的设想是: 用户选择一个路径来保存返回Uri为conte

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

我的设想是:

  • 用户选择一个路径来保存返回Uri为
    content://com.android.externalstorage.documents
    onActivityResult()。我使用
    treeUri.toString()
    将此路径保存到SharedReferences以供以后使用
  • 用户拍摄一张照片,并使用
    DocumentFile.fromtreeeuri(MainActivity.this,Uri.parse(uriString))保存图像
  • 这是我失败的地方,获取一个正确指向图像的文件,带有content://的Uri不会返回现有图像。正确的Uri应该
    file:///storage/emulated/
    我可以使用
    file filePath=new file(Uri.create(saveDir.getUri().toString())将此Uri转换为文件
  • 如何使用从SAF UI获得的Uri获取构造文件或文件所需的Uri

    编辑:是为Android 7.1+引入的,它可以使用InputStream或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) {}
      }
    }
    
    注意:ExiFinInterface将不适用于远程输入流,例如从HttpURLConnection返回的输入流。强烈建议仅将它们与content://或file://URI一起使用


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

    如果Api为24或更高,则使用
    文件描述符将EXIF数据写入先前保存的图像,并使用已知的内容Uri

    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();
                }
            }
        }
    }
    
    这两种方法都可以用于将Exif数据写入保存到设备内存或SD卡的图像。您还可以使用Storage Access Framework中的有效Uri将图像保存到SD卡

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

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