Jetpack Compose中批量上传图片至Firebase Storage并获取URL
问题解答
一、ViewModel中Uri列表处理逻辑的问题与修正
原代码存在的问题
- 数据一致性风险:在协程外部初始化
updatedImageList,若协程执行前state.productImagesList已被其他操作修改,会基于旧数据更新,导致新数据丢失。 - 去重逻辑不严谨:
Uri对象的默认equals判断仅对比实例,若出现内容相同但实例不同的Uri,distinct()无法有效去重,不符合业务需求。
修正后的代码
fun updateSelectedImageList(listOfImages: List<Uri>) { viewModelScope.launch { val updatedImageList = state.productImagesList.toMutableList().apply { addAll(listOfImages) } state = state.copy( productImagesList = updatedImageList.distinctBy { it.toString() } ) } }
修正说明
- 将集合初始化与修改逻辑移到协程内部,确保每次更新都基于最新的
state值,避免数据不一致。 - 使用
distinctBy { it.toString() }替代distinct(),基于Uri的字符串标识去重,更符合实际业务场景。 - 用
apply函数简化集合操作,代码更简洁可读。
二、批量上传多图到Firebase Storage并返回下载URL的实现
1. Repository层实现
假设ProductImages数据类定义如下(用于封装批量上传结果):
data class ProductImages(val downloadUrls: List<Uri>)
完成AdminRepositoryImpl中的addProductImagesToFirebaseStorage方法:
override suspend fun addProductImagesToFirebaseStorage(productImages: List<Uri>): AddProductImagesResponse { return try { val baseStorageRef = categoryImageStorage.reference .child("HomeFeed") .child("Products") val timestamp = System.currentTimeMillis() // 并发上传所有图片,提升上传效率 val downloadUrls = coroutineScope { productImages.mapIndexed { index, imageUri -> async { // 生成唯一的图片存储路径,避免文件名冲突 val imageRef = baseStorageRef.child("Product_${timestamp}_$index") // 上传文件并获取下载URL imageRef.putFile(imageUri).await() imageRef.downloadUrl.await() } }.awaitAll() } Success(ProductImages(downloadUrls)) } catch (e: Exception) { Failure(e) } }
实现说明
- 使用
coroutineScope+async实现并发上传,相比串行上传大幅提升多图上传效率。 - 基于时间戳+索引生成唯一存储路径,避免不同图片的文件名冲突。
- 捕获所有异常并返回
Failure,确保错误能被上层感知处理。
2. ViewModel层调用示例
结合UseCases架构,在ViewModel中处理上传状态:
private val _addProductImagesResponse = mutableStateOf<Response<ProductImages>>(Response.Loading) val addProductImagesResponse: State<Response<ProductImages>> = _addProductImagesResponse fun uploadProductImages(images: List<Uri>) { viewModelScope.launch { _addProductImagesResponse.value = Response.Loading val result = adminRepository.addProductImagesToFirebaseStorage(images) _addProductImagesResponse.value = result } }
3. Composable层状态处理
@Composable fun AddProductImagesToStorage( viewModel: ProductViewModel = hiltViewModel(), onImagesUploaded: (List<Uri>) -> Unit ) { when (val response = viewModel.addProductImagesResponse) { is Response.Loading -> ProgressBar() is Response.Success -> response.data?.let { productImages -> LaunchedEffect(productImages) { onImagesUploaded(productImages.downloadUrls) } } is Response.Failure -> LaunchedEffect(Unit) { println("多图上传失败:${response.e.message}") } } }
内容的提问来源于stack exchange,提问作者Shiva s
相关产品推荐
相关产品推荐

