【问题标题】:JPEG gets corrupt when saving image with exif metadata使用 exif 元数据保存图像时 JPEG 损坏
【发布时间】:2021-09-08 23:21:24
【问题描述】:

我正在开发一个将位图作为 jpeg 图像保存到外部存储的 Android 应用程序。 JPEG 有时会损坏(见下图)。我已经意识到只有在调用 saveExif() 时才会发生损坏(最终)。如果我注释掉 saveExif(),则永远不会发生损坏。这意味着它是由与 EXIF 相关的东西引起的,而不是由压缩过程引起的。 我使用软件 (Bad Peggy) 分析了 jpeg,该软件检测到图像由于数据段过早结束而损坏。

知道怎么解决吗?

这是我最初保存图像的方式:

        lateinit var uri: Uri
        val imageOutStream: OutputStream
        val contentResolver = context.contentResolver

        val mimeType =  "image/jpeg"
        val mediaContentUri = MediaStore.Images.Media.getContentUri(MediaStore.VOLUME_EXTERNAL_PRIMARY)
        val values = ContentValues().apply {
            put(MediaStore.Images.Media.DISPLAY_NAME, fileName)
            put(MediaStore.Images.Media.MIME_TYPE, mimeType)
            put(MediaStore.Images.Media.RELATIVE_PATH, directory)
        }
        contentResolver.run {
            uri = context.contentResolver.insert(mediaContentUri, values)
                            ?: return
            imageOutStream = openOutputStream(uri) ?: return
        }

        try {
            imageOutStream.use { bitmap.compress(Bitmap.CompressFormat.JPEG, photoCompression, it) }
        } catch (e: java.lang.Exception) {
            e.printStackTrace()
        } finally {
            imageOutStream.close()
        }

        try {
            context.contentResolver.openInputStream(uri).use {
                val exif = ExifInterface(context.contentResolver.openFileDescriptor(uri, "rw")!!.fileDescriptor)
                saveExif(exif, context)     //method ads exif metadata to image

            }
        }catch (e: java.lang.Exception){

        }

这是我在存储 JPEG 后添加 Exif 元数据的方式:

private fun saveExif(exif: ExifInterface, context: Context){
            if (referenceWithCaptionExif != "" && notesExif != "") {
                exif.setAttribute(ExifInterface.TAG_USER_COMMENT, "$referenceWithCaptionExif | $notesExif")
            } else {
                exif.setAttribute(ExifInterface.TAG_USER_COMMENT, "$referenceWithCaptionExif$notesExif")
            }
            if (companyExif != "") {
                exif.setAttribute(ExifInterface.TAG_CAMERA_OWNER_NAME, companyExif)
                val yearForExif = SimpleDateFormat("yyyy",
                        Locale.getDefault()).format(Date())
                exif.setAttribute(ExifInterface.TAG_COPYRIGHT, "Copyright (c) $companyExif $yearForExif")
            }
            if (projectExif != "") {
                exif.setAttribute(ExifInterface.TAG_IMAGE_DESCRIPTION, projectExif)
            }
            exif.setAttribute(ExifInterface.TAG_MAKER_NOTE, "Project[$projectExif] Company[$companyExif] " +
                    "Notes[$notesExif] Reference[$referenceExif] ReferenceType[$referenceTypeExif] Coordinates[$coordinatesExif] " +
                    "CoordinateSystem[$coordinateSystemExif] Accuracy[$accuracyExif] Altitude[$altitudeExif] " +
                    "Date[$dateTimeExif] Address[$addressExif]")
            exif.setAttribute(ExifInterface.TAG_ARTIST, "${android.os.Build.MANUFACTURER} ${android.os.Build.MODEL}")
            exif.setAttribute(ExifInterface.TAG_SOFTWARE, context.resources.getString(R.string.app_name))
            exif.setAttribute(ExifInterface.TAG_MAKE, (android.os.Build.MANUFACTURER).toString())
            exif.setAttribute(ExifInterface.TAG_MODEL, (android.os.Build.MODEL).toString())
            exif.setAttribute(ExifInterface.TAG_COMPRESSION, 7.toString())
            exif.setAttribute(ExifInterface.TAG_IMAGE_WIDTH, "${bitmapToProcess.width} px")
            exif.setAttribute(ExifInterface.TAG_IMAGE_LENGTH, "${bitmapToProcess.height} px")
            exif.setAttribute(ExifInterface.TAG_PIXEL_X_DIMENSION, "${bitmapToProcess.width} px")
            exif.setAttribute(ExifInterface.TAG_PIXEL_Y_DIMENSION, "${bitmapToProcess.height} px")
            exif.setAttribute(ExifInterface.TAG_GPS_ALTITUDE, altitudeExif)
            exif.setAttribute(ExifInterface.TAG_GPS_ALTITUDE_REF, 0.toString())
            exif.setAltitude(altitudeMetricExif)
            exif.setLatLong(latitudeWGS84Exif, longitudeWGS84Exif)
            exif.setAttribute(ExifInterface.TAG_GPS_TIMESTAMP, timeGPSExif)
            exif.setAttribute(ExifInterface.TAG_GPS_DATESTAMP, dateGPSExif)
            exif.setAttribute(ExifInterface.TAG_GPS_PROCESSING_METHOD, "GPS")
            exif.setAttribute(ExifInterface.TAG_DATETIME, dateTimeOriginalExif)
            exif.setAttribute(ExifInterface.TAG_DATETIME_ORIGINAL, dateTimeOriginalExif)
            exif.setAttribute(ExifInterface.TAG_DATETIME_DIGITIZED, dateTimeOriginalExif)
            if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.N){
                exif.setAttribute(ExifInterface.TAG_OFFSET_TIME_DIGITIZED, SimpleDateFormat("XXX", Locale.getDefault()).format(Date()))
                exif.setAttribute(ExifInterface.TAG_OFFSET_TIME_ORIGINAL, SimpleDateFormat("XXX", Locale.getDefault()).format(Date()))
                exif.setAttribute(ExifInterface.TAG_OFFSET_TIME, SimpleDateFormat("XXX", Locale.getDefault()).format(Date()))
            }

            exif.saveAttributes()
    }

【问题讨论】:

  • 请告诉您为什么以及如何弄乱 exif。你的意图是什么?
  • 并告诉 uriImageCaptureIntent 与这一切有什么关系。你把我们弄糊涂了
  • 嗨@blackapps,感谢您的回复。我已经修改了代码以使其更清晰。回答您的问题:uriImageCaptureIntent 在代码中,用于检查照片是否是使用第三方应用程序的 Intent 拍摄的。在这种情况下,存储需要以不同的方式处理。但我已将其删除,因为不相关。 saveExif 方法将 exif 数据添加到图像中,我已添加代码)。感谢您的帮助!

标签: android jpeg exif android-external-storage android-exifinterface


【解决方案1】:

您可以一次注释掉每条元数据,然后查看是否有特定的元数据导致损坏。其中一些正在发生很多编程,所以我想知道这是否与错误的字符串类型有关。

【讨论】:

  • 您好,感谢您的回答。我省略了压缩、宽度/高度和像素尺寸元数据,经过几次测试,它似乎可以工作并且不再发生损坏。虽然我不能 100% 确定问题已经解决(需要做进一步的测试),但似乎这个 Exif 元数据确实是导致损坏的原因。谢谢!
  • @MichaelK 太好了!
  • 经过进一步的测试和实验,我可以说损坏变得不那么频繁了(在我看来),但是仍然偶尔会发生。所以我无法完全解决这个问题。如果有人有进一步的建议,我会很感激你的帮助!谢谢!
猜你喜欢
  • 2012-10-15
  • 2021-09-04
  • 2021-12-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-03-19
  • 2015-12-03
  • 2020-06-09
相关资源
最近更新 更多