保存位图Android 8时图像名称和类型错误

时间:2020-06-15 07:28:28

标签: android kotlin bitmap

我有一个有趣的功能,可以将位图另存为PNG或JPG(两者均无法正常工作),但是好像使用的内容值无法正常工作。

  1. 文件名不正确。
  2. 文件类型不正确。

我想念什么? 在Android 10上有效,但在Android 8上无效

fun Bitmap.save(context: Context) {
    val contentResolver = context.contentResolver

    val contentValues = ContentValues().apply {
        put(MediaStore.MediaColumns.DISPLAY_NAME, "test.png")
        put(MediaStore.MediaColumns.TITLE, "test")
        put(MediaStore.MediaColumns.MIME_TYPE, "image/png")
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
            put(MediaStore.MediaColumns.RELATIVE_PATH, Environment.DIRECTORY_PICTURES)
            put(MediaStore.MediaColumns.IS_PENDING, 1)
        }
    }

    val contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI
    val uri = contentResolver.insert(contentUri, contentValues)
    if (uri != null) {
        try {
            contentResolver.openFileDescriptor(uri, "w", null)?.use {
                if (it.fileDescriptor != null) {
                    with(FileOutputStream(it.fileDescriptor)) {
                        compress(
                            Bitmap.CompressFormat.PNG,
                            DEFAULT_IMAGE_QUALITY,
                            this
                        )
                        flush()
                        close()
                    }
                }
            }
        } catch (e: Exception) {
            e.printStackTrace()
        }

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
            contentValues.clear()
            contentValues.put(MediaStore.MediaColumns.IS_PENDING, 0)
            contentResolver.update(uri, contentValues, null, null)
        }

        MediaScannerConnection.scanFile(context, arrayOf(uri.toString()), null, null)
    }
    recycle()
}

实际文件名为 1592205828045 (带有时间戳)

实际文件类型为 jpg 0B -由于未正确保存?

2 个答案:

答案 0 :(得分:1)

您正在创建文件,但仍需要向其中写入Bitmap

fun Bitmap.save(context: Context) {

    ...

    val bitmap = this
    val maxImageQuality = 100

    val uri = contentResolver.insert(contentUri, contentValues)
    if (uri != null) {
        try {
            contentResolver.openFileDescriptor(uri, "w", null)?.use {
                if (it.fileDescriptor != null) {
                    with(FileOutputStream(it.fileDescriptor)) {
                        bitmap.compress(
                            Bitmap.CompressFormat.PNG,
                            maxImageQuality, this
                        )
                        flush()
                        close()
                    }
                }
            }
        } catch (e: Exception) {
            e.printStackTrace()
        }

        // release pending status of the file
        contentValues.clear()
        contentValues.put(MediaStore.Images.Media.IS_PENDING, 0)
        contentResolver.update(uri, contentValues, null, null)

        // notify media scanner there's a new picture
        MediaScannerConnection.scanFile(context, arrayOf(uri.toString()), null, null)
    }
    // don't forget to recycle the bitmap when you don't need it any longer
    bitmap.recycle()
}

答案 1 :(得分:0)

您将必须维护2种将图像保存到共享存储的不同方式。 post涵盖得很好。在较旧的手机中使用Media Store API会导致您描述的问题。一些适合您的代码示例(已在Android 8、10和11上进行了测试)。

将这些添加到您的清单

<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />

<!--  File save functions handles this  -->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
    android:maxSdkVersion="28"
    tools:ignore="ScopedStorage" />

向您的应用添加权限检查(未提供代码)

准备好位图调用后,可以使用以下两个功能之一(取决于应用程序当前运行的手机的SDK版本)

    //TODO - bitmap needs null check
      val bitmap = BitmapFactory.decodeFile(bitmapFile.canonicalPath)

      if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q)
         {
           saveBitmapPreQ(bitmap)
         } else {
           saveBitmapPostQ(bitmap)
         }

最后,这些是 saveBitmapPreQ saveBitmapPostQ

的实现
@Suppress("DEPRECATION") // Check is preformed on function call
private fun saveBitmapPreQ(thisBitmap: Bitmap){
    Log.d("HOME_4", "in pre Q")
    val pictureDirectory = 
   File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES), 
   "MyFolder")

    if (!pictureDirectory.exists()){
        pictureDirectory.mkdir()
    }

    val dateTimeStamp = SimpleDateFormat("yyyyMMddHHmmss").format(Date())
    val name = "Image_$dateTimeStamp"

    val bitmapFile = File(pictureDirectory, "$name.png")

    try {
        val fileOutputStream = bitmapFile.outputStream()
        thisBitmap.compress(Bitmap.CompressFormat.PNG, 100, fileOutputStream)
        fileOutputStream.flush()
        fileOutputStream.close()
    } catch (e: Exception) {
        Log.d("HOME_5", "Pre Q error $e")
    }

}

private fun saveBitmapPostQ(thisBitmap: Bitmap){
    Log.d("HOME_6", "in post Q")
    val dateTimeStamp = SimpleDateFormat("yyyyMMddHHmmss").format(Date())
    val name = "Image_$dateTimeStamp"
    val relativePath = Environment.DIRECTORY_PICTURES + File.separator + "MyFolder"


    val contentValues = ContentValues().apply {
        put(MediaStore.Images.ImageColumns.DISPLAY_NAME, name)

        put(MediaStore.MediaColumns.MIME_TYPE, "image/png")
        put(MediaStore.MediaColumns.TITLE, name)
        put(MediaStore.Images.ImageColumns.RELATIVE_PATH, relativePath)
    }

    val contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI
    var outputStream: OutputStream? = null
    var uri: Uri? = null

    try {
        uri = contentResolver.insert(contentUri, contentValues)

        if (uri == null){
            throw IOException("Failed to create new MediaStore record.")
        }

        outputStream = contentResolver.openOutputStream(uri)

        if (outputStream == null){
            throw IOException("Failed to get output stream.")
        }

        if (!thisBitmap.compress(Bitmap.CompressFormat.PNG, 100, outputStream)){
            throw IOException("Failed to save bitmap.")
        }
    } catch (e: IOException){
        if (uri != null)
        {
            contentResolver.delete(uri, null, null)
        }

        throw IOException(e)
    }

    finally {
        outputStream?.close()
    }
}

我在那里留下了日志消息,以帮助您了解流程。在 saveBitmapPostQ 功能中,我采用了一些快捷方式。请阅读创建新文件标题下的post,以了解如何进一步改善该功能。