如何获取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!!) } ) }
问题原因与解决方案
核心问题
- 直接传入本地URI:
onImageSelected中直接调用addIMageUrlToFireBase传入了本地图片URI,而非Storage返回的云端下载URL。 - 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
相关产品推荐
相关产品推荐

