You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Kotlin结合io fotoapparat库实现拍照后更新Firebase存储与Firestore

问题修复方案

核心问题梳理

  • Fotoapparat拍照后未配置结果回调,无法获取拍摄得到的位图数据
  • 现有onActivityResult是系统相机的回调逻辑,和Fotoapparat库的调用逻辑完全不兼容,不会被触发
  • 创建收据文档得到的docId没有持久化存储,上传照片时找不到需要更新的目标文档
  • 调用的上传方法名和仓库层实际定义的方法名不匹配,上传逻辑根本没有执行
  • 拍照按钮未绑定点击事件,无法触发拍照流程

具体修复步骤

1. 持久化存储当前收据ID

在ScanViewModel中新增变量存储最新创建的收据文档ID:

// ScanViewModel.kt
val currentReceiptId = MutableStateFlow<String?>(null)

修改新增Firestore文档的逻辑,创建成功后保存ID:

binding.button.setOnClickListener {
    val biedronka = "biedronka"
    val price = "20"
    val img = " "
    val identificator = " "

    val from = biedronka
    val value = price
    val image = img
    val idd = identificator

    val db = Firebase.firestore
    val data = hashMapOf(
        "from" to from,
        "value" to value,
        "image" to image,
        "id" to idd
    )

    db.collection("receipts")
        .add(data)
        .addOnSuccessListener { documentReference ->
            Log.d(SCAN_DEBUG, "DocumentSnapshot written with ID: ${documentReference.id}")
            // 保存收据ID到ViewModel
            scanVm.currentReceiptId.value = documentReference.id
            db.collection("receipts")
                .document(documentReference.id)
                .update("id", documentReference.id)
                .addOnSuccessListener {
                }

        }
        .addOnFailureListener { e ->
            Log.w(SCAN_DEBUG, "Error adding document", e)
        }
}

2. 修正Fotoapparat拍照逻辑,添加结果回调

修改takePhoto方法,拿到位图后直接调用上传逻辑,同时给拍照按钮绑定点击事件:

override fun onCreateView(
    inflater: LayoutInflater, container: ViewGroup?,
    savedInstanceState: Bundle?,
): View {
    _binding = FragmentScanBinding.inflate(inflater, container, false)

    createFotoapparat()

    cameraStatus = CameraState.BACK
    flashState = FlashState.OFF
    fotoapparatState = FotoapparatState.OFF

    binding.fabSwitchCamera.setOnClickListener {
        switchCamera()
    }

    binding.fabFlash.setOnClickListener {
        changeFlashState()
    }
    
    // 新增:拍照按钮绑定点击事件
    binding.fabTakePhoto.setOnClickListener {
        takePhoto()
    }
    return binding.root
}

private fun takePhoto() {
    if (hasNoPermissions()) {
        requestPermission()
    }else{
        fotoapparat
            ?.takePicture()
            ?.toBitmap()
            // 新增:拍照结果回调
            ?.whenAvailable { photoResult ->
                photoResult?.bitmap?.let { bitmap ->
                    val stream = ByteArrayOutputStream()
                    val compressSuccess = bitmap.compress(Bitmap.CompressFormat.JPEG, 90, stream)
                    val byteArray = stream.toByteArray()
                    if (compressSuccess) {
                        val receiptId = scanVm.currentReceiptId.value ?: return@let
                        // 调用仓库层上传方法
                        scanVm.uploadReceiptPhoto(byteArray, receiptId)
                    }
                }
            }
    }
}

3. 修正仓库层上传逻辑,传入动态docId

将全局docId改为参数传递,避免空值问题:

fun uploadReceiptPhoto(bytes: ByteArray, docId: String) {
    storage.getReference("receipts")
        .child("${docId}.jpg")
        .putBytes(bytes)
        .addOnCompleteListener{
            Log.d(REPO_DEBUG, "COMPLETE UPLOAD PHOTO")
        }
        .addOnSuccessListener {
            getReceiptPhotoDownloadUrl(it.storage, docId)
        }
        .addOnFailureListener {
            Log.d(REPO_DEBUG, it.message.toString())
        }
}
private fun getReceiptPhotoDownloadUrl(storage: StorageReference, docId: String) {
    storage.downloadUrl
        .addOnSuccessListener {
            updateReceiptPhoto(it.toString(), docId)
        }
        .addOnFailureListener {
            Log.d(REPO_DEBUG, it.message.toString())
        }
}
private fun updateReceiptPhoto(url: String, docId: String) {
    cloud.collection("receipts")
        .document(docId)
        .update("image", url)
        .addOnSuccessListener {
            Log.d(REPO_DEBUG, "UPDATE RECEIPT PHOTO SUCCESS")
        }
        .addOnFailureListener {
            Log.d(REPO_DEBUG, it.message.toString())
        }
}

4. 删除无效代码

原有onActivityResult方法是系统相机的回调逻辑,和Fotoapparat完全无关,可以直接删除。

5. 检查Firebase权限

确保Firebase Storage和Firestore的安全规则允许当前用户读写,测试阶段可临时使用以下规则:
Storage规则

rules_version = '2';
service firebase.storage {
  match /b/{bucket}/o {
    match /{allPaths=**} {
      allow read, write: if request.auth != null;
    }
  }
}

Firestore规则

rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {
    match /{allPaths=**} {
      allow read, write: if request.auth != null;
    }
  }
}

功能验证完成后请修改为更严格的权限规则,避免数据泄露。


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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.30 00:27:05