You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

Android Camera2如何将捕获照片保存为JPG至DCIM目录?

实现相机捕获图像保存到DCIM目录的方案

我来帮你搞定把拍摄的图片保存到/sdcard/DCIM/目录的需求!咱们一步步修改现有代码,核心是通过ImageReader获取图像数据,再写入到DCIM目录的JPG文件:

1. 确保ImageReader配置正确

首先,你的ImageReader需要初始化为JPEG格式,这样才能直接获取到可保存的JPG字节数据。在初始化相机的环节添加这段代码(比如在打开相机前):

private var mImageReader: ImageReader? = null

// 初始化ImageReader,替换成你相机支持的合适尺寸
fun initImageReader(cameraSize: Size) {
    // 参数:宽、高、图像格式、最大图像缓存数
    mImageReader = ImageReader.newInstance(
        cameraSize.width,
        cameraSize.height,
        ImageFormat.JPEG,
        1
    )
    // 设置图像可用监听器,捕获到图像后会回调这里
    mImageReader?.setOnImageAvailableListener(mOnImageAvailableListener, null)
}

2. 实现图像数据的处理与保存

添加ImageReader.OnImageAvailableListener的实现,这里是获取图像并写入文件的核心逻辑:

private val mOnImageAvailableListener = ImageReader.OnImageAvailableListener { reader ->
    // 获取最新捕获的图像
    val image = reader.acquireLatestImage() ?: return@OnImageAvailableListener
    
    // 从Image中提取JPEG字节数据
    val plane = image.planes[0]
    val buffer = plane.buffer
    val imageBytes = ByteArray(buffer.remaining())
    buffer.get(imageBytes)
    
    // 保存到DCIM目录
    saveImageToDCIM(imageBytes)
    
    // 务必关闭Image,避免内存泄漏!
    image.close()
}

// 保存图片到DCIM目录的工具函数
private fun saveImageToDCIM(imageBytes: ByteArray) {
    // 获取系统DCIM目录
    val dcimDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM)
    // 用时间戳命名,避免文件名重复
    val fileName = "IMG_${System.currentTimeMillis()}.jpg"
    val targetFile = File(dcimDir, fileName)

    try {
        // 写入文件
        FileOutputStream(targetFile).use { outputStream ->
            outputStream.write(imageBytes)
            outputStream.flush()
        }
        
        // 通知系统媒体库更新,这样图库能立刻看到新图片
        MediaScannerConnection.scanFile(
            applicationContext,
            arrayOf(targetFile.absolutePath),
            arrayOf("image/jpeg"),
            null
        )
        
        Toast.makeText(applicationContext, "图片已保存到DCIM", Toast.LENGTH_SHORT).show()
    } catch (e: IOException) {
        e.printStackTrace()
        Toast.makeText(applicationContext, "保存失败:${e.message}", Toast.LENGTH_SHORT).show()
    }
}

3. 权限配置不能忘

  • 静态权限声明:在AndroidManifest.xml里添加必要权限:
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" android:maxSdkVersion="28" />
<!-- Android 10+ 可以用MediaStore替代,或者申请MANAGE_EXTERNAL_STORAGE权限(不推荐) -->
  • 动态权限申请:Android 6.0(API 23)及以上,需要在代码中动态申请CAMERAWRITE_EXTERNAL_STORAGE权限,确保用户授权后再打开相机。

4. 检查原有代码的适配

你原来的takePicturemSessionCallback逻辑基本没问题,只要确保ImageReader的surface被正确添加到捕获请求中(你已经做到了)。另外,mCaptureCallbackonCaptureCompleted可以保持原样,用来关闭会话。

额外注意(Android 10+ 适配)

如果你的应用目标SDK是29及以上,由于Scoped Storage限制,直接写入DCIM目录需要特殊处理,更合规的方式是用MediaStore插入图片:

// Android 10+ 用MediaStore保存的示例
private fun saveImageToDCIMForAndroidQ(imageBytes: ByteArray) {
    val contentValues = ContentValues().apply {
        put(MediaStore.Images.Media.DISPLAY_NAME, "IMG_${System.currentTimeMillis()}.jpg")
        put(MediaStore.Images.Media.MIME_TYPE, "image/jpeg")
        put(MediaStore.Images.Media.RELATIVE_PATH, Environment.DIRECTORY_DCIM)
    }

    val resolver = applicationContext.contentResolver
    val uri = resolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, contentValues)
    
    uri?.let {
        resolver.openOutputStream(it)?.use { outputStream ->
            outputStream.write(imageBytes)
            outputStream.flush()
        }
        Toast.makeText(applicationContext, "图片已保存到DCIM", Toast.LENGTH_SHORT).show()
    } ?: run {
        Toast.makeText(applicationContext, "保存失败", Toast.LENGTH_SHORT).show()
    }
}

这样修改后,点击拍摄按钮捕获的图像就会自动保存到DCIM目录啦!

内容的提问来源于stack exchange,提问作者hyunwookcho

火山引擎 最新活动