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

如何获取Firebase Storage上传图片的URL并写入Firestore?

问题:将Firebase Storage图片URL存入Firestore而非本地URL

我希望将Firebase Storage中保存的图片URL传入Firebase数据库,但目前Firestore数据库中存储的却是选中图片的本地URL。以下是我的代码实现:

仓库实现

override suspend fun addImageToFirebaseStorage(userData: UserData,
    imageUri: Uri,
    ): Resource<Uri> {

    val username = userData.displayName
    return try {
        val downloadUrl = storage.reference.child(IMAGES)
            .child("$username.jpg")
            .putFile(imageUri).await()
            .storage.downloadUrl.await()

        Resource.Success(downloadUrl)
    } catch (e: Exception) {
        Resource.Error(e.message.toString())
    }
}

override suspend fun addImageToFireStore(userData: UserData,
    downloadUri: Uri,
): Resource<Boolean> {

    return try {
        db.collection(COLLECTION_NAME).document(userData.displayName)
            .update(
                mapOf(
                    userData.profilePictureUri to downloadUri.toString(),
                    CREATED_AT to FieldValue.serverTimestamp()
                )
            ).await()

        Resource.Success(true)
    } catch (e: Exception) {
        Resource.Error(e.message.toString())
    }
}

ViewModel代码

fun addImageToFirebaseStorage(
    userData: UserData,
    imageUri: Uri,
) = viewModelScope.launch {
    userDataRepository.addImageToFirebaseStorage(userData, imageUri)
}

fun addIMageUrlToFireBase(
    userData: UserData,
    downloadUri: Uri,
) = viewModelScope.launch {
    userDataRepository.addImageToFireStore(userData, downloadUri)
}

ViewModel的Composable初始化代码

composable(Screens.AboutMe.route) {
    val viewModel: SharedViewModel = hiltViewModel()
    val state = viewModel.userDataState.collectAsState(initial = null)

    val newUserData by viewModel.newUserData
    UserDataScreen(
        state = state.value?.copy() ?: AboutState(),
        newUserData = newUserData,
        onValueChange = viewModel::updateUiState,
        onSaveButtonClicked = { userData, uri ->
            if (uri != null) {
                viewModel.addImageToFirebaseStorage(userData, uri)
            }
            viewModel.saveData(userData)
        },
        onImageSelected = { uri ->
            viewModel.updateUiState(newUserData.userData.copy(profilePictureUri = uri.toString()))
            viewModel.addIMageUrlToFireBase(newUserData.userData.copy(profilePictureUri = uri.toString()), uri!!)
        }
    )
}

问题原因与解决方案

核心问题

  1. 直接传入本地URI:onImageSelected中直接调用addIMageUrlToFireBase传入了本地图片URI,而非Storage返回的云端下载URL。
  2. Firestore字段名错误:addImageToFireStore用userData.profilePictureUri作为字段名,导致把本地URI当作键存入数据库,而非使用固定字段名。

修复步骤

1. 修正仓库的addImageToFireStore方法

将字段名改为固定字符串,确保更新正确字段:

override suspend fun addImageToFireStore(userData: UserData, downloadUri: Uri): Resource<Boolean> {
    return try {
        db.collection(COLLECTION_NAME).document(userData.displayName)
            .update(
                mapOf(
                    "profilePictureUri" to downloadUri.toString(), // 改为固定字段名
                    CREATED_AT to FieldValue.serverTimestamp()
                )
            ).await()
        Resource.Success(true)
    } catch (e: Exception) {
        Resource.Error(e.message.toString())
    }
}

2. 调整ViewModel逻辑,先上传再存URL

合并上传与存URL的逻辑,确保拿到云端URL后再写入Firestore:

fun addImageToFirebaseStorageAndSaveUrl(
    userData: UserData,
    imageUri: Uri,
) = viewModelScope.launch {
    val uploadResult = userDataRepository.addImageToFirebaseStorage(userData, imageUri)
    if (uploadResult is Resource.Success) {
        // 上传成功后存入云端URL
        userDataRepository.addImageToFireStore(userData, uploadResult.data)
        // 更新UI状态为云端URL
        updateUiState(userData.copy(profilePictureUri = uploadResult.data.toString()))
    }
}

3. 修改Composable调用逻辑

移除onImageSelected中的直接存URL操作,改为在保存时触发上传+存URL流程:

composable(Screens.AboutMe.route) {
    val viewModel: SharedViewModel = hiltViewModel()
    val state = viewModel.userDataState.collectAsState(initial = null)

    val newUserData by viewModel.newUserData
    UserDataScreen(
        state = state.value?.copy() ?: AboutState(),
        newUserData = newUserData,
        onValueChange = viewModel::updateUiState,
        onSaveButtonClicked = { userData, uri ->
            if (uri != null) {
                // 调用合并后的方法,先上传再存URL
                viewModel.addImageToFirebaseStorageAndSaveUrl(userData, uri)
            }
            viewModel.saveData(userData)
        },
        onImageSelected = { uri ->
            // 仅更新UI显示本地预览,不直接写入数据库
            viewModel.updateUiState(newUserData.userData.copy(profilePictureUri = uri.toString()))
        }
    )
}

说明

修改后,只有当图片成功上传到Firebase Storage并获取到云端下载URL后,才会将该URL存入Firestore;图片选择时仅更新UI显示本地预览,避免存入无效的本地URI。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.02 09:05:42