Я работаю над приложением для Android, в котором пользователи могут выбирать несколько фотографий из галереи своего устройства и загружать их на внутренний сервер. Для этого я использую PickMultipleVisualMedia()
, чтобы получить URI выбранных изображений, а затем передаю их работнику для загрузки.
Вот как я реализовал средство выбора фотографий и работника:
Реализация средства выбора фотографий:
private fun launchNewPhotoPicker(){
newPicker.launch(PickVisualMediaRequest(ActivityResultContracts.PickVisualMedia.ImageOnly))
}
val newPicker = registerForActivityResult(ActivityResultContracts.PickMultipleVisualMedia(5)) { uris ->
if (uris != null) {
Log.d("PhotoPicker", "Selected URIs: ${uris}")
runUploadImageWorker(uris)
} else {
Log.d("PhotoPicker", "No media selected")
}
}
private fun runUploadImageWorker(uris: List<Uri>) {
val strUris = uris.map { it.toString() }.toTypedArray()
val data = Data.Builder()
.putStringArray("uris", strUris)
.build()
val uploadImageRequest = OneTimeWorkRequestBuilder<UploadImageWorker>()
.setInputData(data)
.build()
WorkManager.getInstance(requireContext()).enqueue(uploadImageRequest)
}
Реализация работника:
class UploadImageWorker(appContext: Context, workerParams: WorkerParameters) :
Worker(appContext, workerParams) {
override fun doWork(): Result {
val uris = inputData.getStringArray("uris") ?: return Result.failure()
val client = okhttp3.OkHttpClient()
for (uriString in uris) {
val uri = Uri.parse(uriString)
val inputStream = applicationContext.contentResolver.openInputStream(uri)
val requestBody = inputStream?.readBytes()?.toRequestBody("image/*".toMediaTypeOrNull())
val body = requestBody?.let {
MultipartBody.Part.createFormData("picture", UUID.randomUUID().toString(), it)
} ?: return Result.failure()
val multipartBody = MultipartBody.Builder()
.setType(MultipartBody.FORM)
.addPart(body)
.build()
val request = okhttp3.Request.Builder()
.url("https://mywonderfullfakeurl.com/api/pictures")
.post(multipartBody)
.build()
val response = client.newCall(request).execute()
if (!response.isSuccessful) {
val errorBody = response.body?.string() ?: "Unknown error"
Log.d("error", errorBody)
return Result.failure()
}
}
return Result.success()
}
}
Однако я заметил, что в изображениях, загруженных на мой серверный сервер, отсутствуют данные EXIF. Как я могу гарантировать сохранение данных EXIF при загрузке изображений из приложения Android с помощью Kotlin?
Я полностью согласен с вашим предложением. Однако я сталкиваюсь с проблемами при получении данных EXIF из URI в моем приложении. Всякий раз, когда я пытаюсь использовать интерфейс EXIF для чтения данных, я сталкиваюсь с ошибкой «ENOENT» (нет такого файла или каталога). Не могли бы вы предоставить руководство или фрагмент кода о том, как правильно получить информацию EXIF из URI, не столкнувшись с этой ошибкой?
Я уже опробовал подход Maveňツ, и, как вы сказали, он не сработал. Я рассмотрю альтернативные методы для достижения этой цели. А затем обновите этот вопрос
Используйте подход Maven. Вам нужно лишь немного измениться, чтобы это заработало. Просто выясните, где именно что-то идет не так.
Даже если вы реализуете ACCESS_MEDIA_LOCATION, поставщик, который вы получаете с помощью PickVisualMedia() и PickMultipleVisualMedia(), будет redact out
всю информацию о местоположении exif.
Это особенность ;-).
Вам лучше использовать OpenDocument() или OpenMultipleDocuments().
Я OpenDocument()
попробую и посмотрю, решит ли это проблему.
Чтобы сохранить данные EXIF при загрузке изображений из приложения Android с помощью Kotlin, вы можете использовать следующий метод, вдохновленный @Maveňツ, а также использовать временный файл, чтобы избежать ошибок ENOENT
.
Кроме того, как отметил @blackapps, использование OpenMultipleDocuments()
вместо PickMultipleVisualMedia()
имеет решающее значение для предотвращения нулевых данных EXIF при чтении из URI.
Реализация средства выбора фотографий:
private fun launchNewPhotoPicker(){
newPiker.launch(arrayOf("image/jpeg"))
}
private val newPiker=registerForActivityResult(ActivityResultContracts.OpenMultipleDocuments()) { uris ->
if (uris != null) {
Log.d("PhotoPicker", "Selected URI: ${uris}")
//launch the worker to upload the images
runUploadImageWorker(uris)
} else {
Log.d("PhotoPicker", "No media selected")
}
}
Реализация работника:
override fun doWork(): Result {
val uris = inputData.getStringArray("uris") ?: return Result.failure()
val client = okhttp3.OkHttpClient()
for (uriString in uris) {
val uri = Uri.parse(uriString)
// create temp file
val uuid = UUID.randomUUID().toString()
val file = File(applicationContext.cacheDir, uuid)
// copy selected file to temp file
uri?.let { applicationContext.contentResolver.openInputStream(it) }.use { input ->
file.outputStream().use { output ->
input?.copyTo(output)
}
}
val inputStream = file.inputStream()
val requestBody = inputStream.readBytes().toRequestBody("image/*".toMediaTypeOrNull())
val body = requestBody.let {
MultipartBody.Part.createFormData("picture", UUID.randomUUID().toString(), it)
}
val multipartBodyBuilder = MultipartBody.Builder()
.setType(MultipartBody.FORM)
.addPart(body)
// Preserving EXIF data
val exifInterface = ExifInterface(file)
val tagsToCheck = arrayOf(
ExifInterface.TAG_DATETIME,
ExifInterface.TAG_GPS_LATITUDE,
ExifInterface.TAG_GPS_LONGITUDE,
ExifInterface.TAG_GPS_ALTITUDE
)
for (tag in tagsToCheck) {
exifInterface.getAttribute(tag)?.let {
multipartBodyBuilder.addFormDataPart("exif_$tag", it)
}
}
val multipartBody = multipartBodyBuilder.build()
// don't forget to delete the temporary file
file.delete()
val request = okhttp3.Request.Builder()
.url("https://mywonderfullfakeurl.com/api/pictures")
.post(multipartBody)
.build()
val response = client.newCall(request).execute()
if (!response.isSuccessful) {
val errorBody = response.body?.string() ?: "Unknown error"
Log.d("error", errorBody)
return Result.failure()
}
}
return Result.success()
}
В этой исправленной версии:
OpenMultipleDocuments()
вместо PickMultipleVisualMedia()
для получения данных EXIF.ExifInterface
.MultipartBody
с помощью addFormDataPart
.Это гарантирует, что загруженные изображения сохранят выбранные данные EXIF при отправке на внутренний сервер.
Вам не нужно сначала загружать, а потом на вашем сервере обнаружить, что информация о местоположении отсутствует. Лучше просто попробуйте прочитать exif-информацию, открыв полученный uri самостоятельно.