Запись данных EXIF ​​в изображение, сохраненное с помощью класса DocumentFile

Мне нужно получить файл из DocumentFile или Uri с правильной схемой, а не с content://com.android.externalstorage.documents/tree/primary:, если выбрана основная память устройства. Чтобы получить файл или абсолютный путь к изображению, мне нужен файл с file: /// storage / emulated / 0 или storage / emulated / 0, но я не смог найти способ получить правильный Uri для создания файла для записи данных EXIF ​​в изображение.

Мой сценарий:

  1. Пользователь выбирает путь для сохранения изображений, который возвращает Uri с content://com.android.externalstorage.documents onActivityResult (). Я сохраняю этот путь с treeUri.toString() в SharedPreferences для использования в дальнейшем.
  2. Пользователь делает снимок, и изображение сохраняется с DocumentFile.fromTreeUri(MainActivity.this, Uri.parse(uriString));
  3. Здесь я не могу получить файл, который правильно указывает на изображение, Uri с content: // не возвращает существующее изображение. Правильный Uri должен file:///storage/emulated/, и я могу преобразовать этот Uri в файл, используя File filePath = new File(URI.create(saveDir.getUri().toString()));

Как я могу получить Uri, необходимый для создания файла или файла с использованием Uri, полученного из пользовательского интерфейса SAF?

РЕДАКТИРОВАТЬ: ExifInterface Библиотека поддержки представлена ​​для 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 не будет работать с удаленными потоками InputStream, например, возвращаемыми из HttpURLConnection. Настоятельно рекомендуется использовать их только с URI content: // или file: //.

Фрагмент выше, очевидно, считывает данные, поскольку он открывает InputStream для чтения данных. Мне нужно иметь возможность записывать данные EXIF ​​в файлы JPEG.


person Thracian    schedule 27.09.2017    source источник


Ответы (1)


Ответ на запись данных Exif в ранее сохраненное изображение с известным Uri содержимого с использованием FileDescriptor, если Api равен 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, необходимо использовать буферный файл и сохранить этот буферный файл в фактическом месте с помощью DocumentFile после завершения записи данных Exif.

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-карте. Вы также можете сохранить изображение на SD-карту, используя действительный Uri из Storage Access Framework.

Я также нашел способ получить абсолютный путь для памяти и SD-карты из содержимого Uri, но это не имеет отношения к этому вопросу, и использование Uri вместо абсолютного пути приветствуется и не приводит к незамеченным ошибкам, я также не смог сохранить изображение в SD-карта с абсолютным путем, только чтение с нее.

person Thracian    schedule 15.10.2017