2

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

我的情况是:

  1. 用户选择保存与content://com.android.externalstorage.documents onActivityResult返回URI()图像的路径。我将此路径保存到SharedPreferences中以便稍后使用treeUri.toString()
  2. 用户拍照和图像保存DocumentFile.fromTreeUri(MainActivity.this, Uri.parse(uriString));
  3. 这其中,我失败了,获得该正确地指向图像文件,乌里与内容://不返回现有image.Correct URI应该file:///storage/emulated/,我可以将此Uri转换为使用的文件File filePath = new File(URI.create(saveDir.getUri().toString()));

如何获取使用Uri构建文件或文件所需的Uri,我是从SAF UI获得的?

编辑:ExifInterface Support Library是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) {} 
    } 
} 

注: ExifInterface不会与远程InputStreams,如那些从HttpURLConnection的返回工作。强烈建议只在content://或file:// URIs中使用它们。

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

回答

1

答案使用FileDescriptor如果API是写的Exif数据,以前保存的图像,并与已知的内容开放的24个或以上

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写入实际位置。

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卡的图像。您还可以使用存储访问框架中的有效Uri将图像保存到SD卡。

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