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卡,只能從中讀取。