Firebase多图上传异常:循环上传仅最后一张图片重复上传
解决Firebase循环上传多张图片时仅最后一张重复上传的问题
这个问题我之前也碰到过,核心原因是Java匿名内部类的变量捕获机制在搞鬼,结合异步任务的特性就导致了所有上传请求都复用了最后一次循环的图片URI。
问题根源
在你的foreach循环里,imgUrl是循环迭代变量,而匿名内部类(比如Continuation和OnCompleteListener)捕获的是这个变量的引用,不是当前迭代的具体值。因为Firebase的上传是异步操作,当这些异步任务真正执行时,循环已经跑完了,所有任务都指向了最后一次迭代的imgUrl,自然就只会重复上传最后一张图。
另外还要注意:你的count变量如果没有在每次循环里递增,所有图片会被上传到同一个Storage路径下,后面的会覆盖前面的,这也会加剧重复的错觉。
解决方案:捕获当前迭代的变量副本
在循环内部创建一个局部变量来保存当前迭代的图片URI,让匿名内部类捕获这个局部变量的独立实例,就能解决问题。
方案一:增强for循环+局部变量
// 假设你的图片URI列表是imgUrlsList for (Uri imgUrl : imgUrlsList) { // 关键:创建局部变量保存当前迭代的URI,避免引用共享 final Uri currentImgUrl = imgUrl; final StorageReference storageReference = mStorageReference.child("/photos/photo" + (count + 1)); UploadTask uploadTask = storageReference.putFile(currentImgUrl); Task<Uri> urlTask = uploadTask.continueWithTask(new Continuation<UploadTask.TaskSnapshot, Task<Uri>>() { @Override public Task<Uri> then(@NonNull Task<UploadTask.TaskSnapshot> task) throws Exception { if (!task.isSuccessful()) { throw task.getException(); } return storageReference.getDownloadUrl(); } }).addOnCompleteListener(new OnCompleteListener<Uri>() { @Override public void onComplete(@NonNull Task<Uri> task) { if (task.isSuccessful()) { Uri downloadUri = task.getResult(); addPhoto(downloadUri.toString()); } else { Log.d(TAG, "onFailure: Photo upload failed."); Toast.makeText(mContext, "Photo upload failed ", Toast.LENGTH_SHORT).show(); } } }); count++; // 必须在循环内递增count,保证每个图片路径唯一 }
方案二:普通索引循环(更直观)
用索引遍历列表,直接通过索引获取当前迭代的URI,也能避免引用共享的问题:
List<Uri> imgUrlsList = /* 你的URI列表 */; for (int i = 0; i < imgUrlsList.size(); i++) { Uri currentImgUrl = imgUrlsList.get(i); // 直接用i+1作为图片序号,不用额外维护count变量 final StorageReference storageReference = mStorageReference.child("/photos/photo" + (i + 1)); UploadTask uploadTask = storageReference.putFile(currentImgUrl); // 后续代码和上面一致,使用currentImgUrl Task<Uri> urlTask = uploadTask.continueWithTask(new Continuation<UploadTask.TaskSnapshot, Task<Uri>>() { @Override public Task<Uri> then(@NonNull Task<UploadTask.TaskSnapshot> task) throws Exception { if (!task.isSuccessful()) { throw task.getException(); } return storageReference.getDownloadUrl(); } }).addOnCompleteListener(new OnCompleteListener<Uri>() { @Override public void onComplete(@NonNull Task<Uri> task) { if (task.isSuccessful()) { Uri downloadUri = task.getResult(); addPhoto(downloadUri.toString()); } else { Log.d(TAG, "onFailure: Photo upload failed."); Toast.makeText(mContext, "Photo upload failed ", Toast.LENGTH_SHORT).show(); } } }); }
进阶:监听所有上传任务完成
如果需要在所有图片都上传完成后做统一操作(比如通知用户),可以把所有上传任务加入列表,用Tasks.whenAll监听:
List<Task<?>> uploadTasks = new ArrayList<>(); for (Uri imgUrl : imgUrlsList) { final Uri currentImgUrl = imgUrl; final StorageReference storageReference = mStorageReference.child("/photos/photo" + (count + 1)); UploadTask uploadTask = storageReference.putFile(currentImgUrl); Task<Uri> urlTask = uploadTask.continueWithTask(new Continuation<UploadTask.TaskSnapshot, Task<Uri>>() { @Override public Task<Uri> then(@NonNull Task<UploadTask.TaskSnapshot> task) throws Exception { if (!task.isSuccessful()) { throw task.getException(); } return storageReference.getDownloadUrl(); } }).addOnCompleteListener(new OnCompleteListener<Uri>() { @Override public void onComplete(@NonNull Task<Uri> task) { if (task.isSuccessful()) { Uri downloadUri = task.getResult(); addPhoto(downloadUri.toString()); } else { Log.d(TAG, "onFailure: Photo upload failed."); Toast.makeText(mContext, "Photo upload failed ", Toast.LENGTH_SHORT).show(); } } }); uploadTasks.add(urlTask); count++; } // 所有任务完成后的回调 Tasks.whenAll(uploadTasks).addOnCompleteListener(new OnCompleteListener<Void>() { @Override public void onComplete(@NonNull Task<Void> task) { if (task.isSuccessful()) { Toast.makeText(mContext, "所有图片上传完成!", Toast.LENGTH_SHORT).show(); } else { Log.d(TAG, "onFailure: 部分图片上传失败"); } } });
内容的提问来源于stack exchange,提问作者Her win
相关产品推荐
相关产品推荐

