将多个图像上传到 Kotlin 中的 Firebase 存储时,While 循环似乎不适用于 .putFile
While loop doesn't seem to work with .putFile when uploading multiple images to Firebase storage in Kotlin
我一直在尝试将多张图片上传到 Firebase
存储。但是,我无法成功完成。我可以成功将图像(单张)上传到存储并将图像的 URL 添加到 Firestore
,现在我修改了我的代码以上传最多五张图像,它可以是任意数量图片从 1 到 5.
R.id.btn_submit -> {
if (validateDetails()) {
uploadImage()
}
}
以上代码,在验证字段后调用以下函数,然后调用函数 uploadImageToCloudStorage
。 mSelectedImageFileUriList
是 private var mSelectedImageFileUriList: MutableList<Uri?>? = null
。一切似乎都正常工作。
private fun uploadImage() {
showProgressDialog(resources.getString(R.string.please_wait))
FirestoreClass().uploadImageToCloudStorage(
this@AddProductActivity,
mSelectedImageFileUriList,
Constants.PRODUCT_IMAGE,
Constants.PRODUCT_IMAGE_DIRECTORY_NAME,
et_product_title.text.toString().trim { it <= ' ' }
)
}
以下代码是我猜错的地方。
fun uploadImageToCloudStorage(
activity: AddProductActivity,
imageFileURI: MutableList<Uri?>?,
imageType: String,
directoryName: String,
title: String
) {
var i = 0
val imageURLList = ArrayList<String>()
val itr = imageFileURI?.iterator()
if (itr != null) {
while (itr.hasNext()) {
val sRef: StorageReference = FirebaseStorage.getInstance().getReference(
"/$directoryName/" + imageType + "." + Constants.getFileExtension(
activity,
imageFileURI[i]
)
)
sRef.putFile(imageFileURI[i]!!)
.addOnSuccessListener { taskSnapshot ->
taskSnapshot.metadata!!.reference!!.downloadUrl
.addOnSuccessListener { uri ->
if (i < imageFileURI.size) {
i += 1
imageURLList.add(uri.toString())
} else {
activity.imageUploadSuccess(imageURLList)
}
}
}
.addOnFailureListener { exception ->
activity.hideProgressDialog()
Log.e(
activity.javaClass.simpleName,
exception.message,
exception
)
}
}
} else {
Toast.makeText(
activity,
"There is no images in the ArrayList of URI",
Toast.LENGTH_SHORT
).show()
}
}
编辑:收到第一个答案后。
我创建了一个 QueueSyn.kt
文件并在答案中添加了代码。图像和按钮更改为activity
class AddProductActivity : BaseActivity(), View.OnClickListener, QueueSyncCallback {
按下按钮时调用以下函数。
private fun uploadProductImage() {
showProgressDialog(resources.getString(R.string.please_wait))
QueueSync(
mSelectedImageFileUriList,
Constants.PRODUCT_IMAGE,
Constants.PRODUCT_IMAGE_DIRECTORY_NAME,
et_product_title.text.toString().trim { it <= ' ' },
this
).startUploading()
}
我在classAddProductActivity
里面也实现了这两个方法,但是不知道里面应该放些什么。
override fun completed(successList: MutableList<Uri>, failureList: MutableList<Uri>) {
TODO("Not yet implemented")
}
override fun getFileExtension(uri: Uri): String {
TODO("Not yet implemented")
}
错误:
这应该有效
import android.net.Uri
import com.google.firebase.storage.FirebaseStorage
import com.google.firebase.storage.StorageReference
import java.util.*
import kotlin.collections.ArrayList
interface QueueSyncCallback {
fun completed(successList: MutableList<Uri>, failureList: MutableList<Uri>)
fun getFileExtension(uri: Uri): String
}
class QueueSync(
imageFileURI: MutableList<Uri?>?,
private val imageType: String,
private val directoryName: String,
private val title: String,
private val callback: QueueSyncCallback,
private val maxActive: Int = 5
) {
private val queue: LinkedList<Uri> = LinkedList()
private val runningQueue: MutableList<Uri> = Collections.synchronizedList(
object : ArrayList<Uri>() {
override fun remove(element: Uri): Boolean {
val removed = super.remove(element)
if (isEmpty() && queue.isEmpty()) {
callback.completed(successList, failureList)
} else if (queue.isNotEmpty()) {
addToRunningQueue()
}
return removed
}
}
)
private val successList: MutableList<Uri> = Collections.synchronizedList(ArrayList())
private val failureList: MutableList<Uri> = Collections.synchronizedList(ArrayList())
init {
if (imageFileURI != null)
for (uri in imageFileURI) {
if (uri != null)
queue.add(uri)
}
}
private fun getLocation(uri: Uri) = "/$directoryName/$imageType.${callback.getFileExtension(uri)}"
fun startUploading() {
var i = 0
if (queue.isEmpty()) {
callback.completed(successList, failureList)
return
}
while (i < maxActive && queue.isNotEmpty()) {
addToRunningQueue()
i++
}
}
private fun addToRunningQueue() {
val uri = queue.poll()!!
runningQueue.add(uri)
uploadImageToCloudStorage(uri)
}
private fun uploadImageToCloudStorage(locationUri: Uri) {
val sRef: StorageReference = FirebaseStorage.getInstance().getReference(getLocation(locationUri))
sRef.putFile(locationUri)
.addOnSuccessListener { taskSnapshot ->
taskSnapshot.metadata!!.reference!!.downloadUrl
.addOnSuccessListener { uri ->
successList.add(uri)
runningQueue.remove(locationUri)
}
}
.addOnFailureListener {
failureList.add(locationUri)
runningQueue.remove(locationUri)
}
}
}
由于您的需要需要使用线程,所以为了防止竞争条件,我不得不使用 Collections.synchronizedList
。要使用它,您需要在 activity 中实现 QueueSyncCallback 并将其作为对 QueueSync 的引用传递。如果要以任何方式访问视图,请确保 completed
中编写的任何代码都包含在 runOnMainThread
中,因为 completed
不会 运行 在主线程上我所知。这应该可行,但我无法对其进行测试,因为它基于您当前的代码。
编辑:-编辑后回复
override fun completed(successList: MutableList<Uri>, failureList: MutableList<Uri>) {
imageUploadSuccess(successList)
hideProgressDialog()
}
override fun getFileExtension(uri: Uri): String {
Constants.getFileExtension(this, imageFileURI[i])
}
我一直在尝试将多张图片上传到 Firebase
存储。但是,我无法成功完成。我可以成功将图像(单张)上传到存储并将图像的 URL 添加到 Firestore
,现在我修改了我的代码以上传最多五张图像,它可以是任意数量图片从 1 到 5.
R.id.btn_submit -> {
if (validateDetails()) {
uploadImage()
}
}
以上代码,在验证字段后调用以下函数,然后调用函数 uploadImageToCloudStorage
。 mSelectedImageFileUriList
是 private var mSelectedImageFileUriList: MutableList<Uri?>? = null
。一切似乎都正常工作。
private fun uploadImage() {
showProgressDialog(resources.getString(R.string.please_wait))
FirestoreClass().uploadImageToCloudStorage(
this@AddProductActivity,
mSelectedImageFileUriList,
Constants.PRODUCT_IMAGE,
Constants.PRODUCT_IMAGE_DIRECTORY_NAME,
et_product_title.text.toString().trim { it <= ' ' }
)
}
以下代码是我猜错的地方。
fun uploadImageToCloudStorage(
activity: AddProductActivity,
imageFileURI: MutableList<Uri?>?,
imageType: String,
directoryName: String,
title: String
) {
var i = 0
val imageURLList = ArrayList<String>()
val itr = imageFileURI?.iterator()
if (itr != null) {
while (itr.hasNext()) {
val sRef: StorageReference = FirebaseStorage.getInstance().getReference(
"/$directoryName/" + imageType + "." + Constants.getFileExtension(
activity,
imageFileURI[i]
)
)
sRef.putFile(imageFileURI[i]!!)
.addOnSuccessListener { taskSnapshot ->
taskSnapshot.metadata!!.reference!!.downloadUrl
.addOnSuccessListener { uri ->
if (i < imageFileURI.size) {
i += 1
imageURLList.add(uri.toString())
} else {
activity.imageUploadSuccess(imageURLList)
}
}
}
.addOnFailureListener { exception ->
activity.hideProgressDialog()
Log.e(
activity.javaClass.simpleName,
exception.message,
exception
)
}
}
} else {
Toast.makeText(
activity,
"There is no images in the ArrayList of URI",
Toast.LENGTH_SHORT
).show()
}
}
编辑:收到第一个答案后。
我创建了一个 QueueSyn.kt
文件并在答案中添加了代码。图像和按钮更改为activity
class AddProductActivity : BaseActivity(), View.OnClickListener, QueueSyncCallback {
按下按钮时调用以下函数。
private fun uploadProductImage() {
showProgressDialog(resources.getString(R.string.please_wait))
QueueSync(
mSelectedImageFileUriList,
Constants.PRODUCT_IMAGE,
Constants.PRODUCT_IMAGE_DIRECTORY_NAME,
et_product_title.text.toString().trim { it <= ' ' },
this
).startUploading()
}
我在classAddProductActivity
里面也实现了这两个方法,但是不知道里面应该放些什么。
override fun completed(successList: MutableList<Uri>, failureList: MutableList<Uri>) {
TODO("Not yet implemented")
}
override fun getFileExtension(uri: Uri): String {
TODO("Not yet implemented")
}
错误:
这应该有效
import android.net.Uri
import com.google.firebase.storage.FirebaseStorage
import com.google.firebase.storage.StorageReference
import java.util.*
import kotlin.collections.ArrayList
interface QueueSyncCallback {
fun completed(successList: MutableList<Uri>, failureList: MutableList<Uri>)
fun getFileExtension(uri: Uri): String
}
class QueueSync(
imageFileURI: MutableList<Uri?>?,
private val imageType: String,
private val directoryName: String,
private val title: String,
private val callback: QueueSyncCallback,
private val maxActive: Int = 5
) {
private val queue: LinkedList<Uri> = LinkedList()
private val runningQueue: MutableList<Uri> = Collections.synchronizedList(
object : ArrayList<Uri>() {
override fun remove(element: Uri): Boolean {
val removed = super.remove(element)
if (isEmpty() && queue.isEmpty()) {
callback.completed(successList, failureList)
} else if (queue.isNotEmpty()) {
addToRunningQueue()
}
return removed
}
}
)
private val successList: MutableList<Uri> = Collections.synchronizedList(ArrayList())
private val failureList: MutableList<Uri> = Collections.synchronizedList(ArrayList())
init {
if (imageFileURI != null)
for (uri in imageFileURI) {
if (uri != null)
queue.add(uri)
}
}
private fun getLocation(uri: Uri) = "/$directoryName/$imageType.${callback.getFileExtension(uri)}"
fun startUploading() {
var i = 0
if (queue.isEmpty()) {
callback.completed(successList, failureList)
return
}
while (i < maxActive && queue.isNotEmpty()) {
addToRunningQueue()
i++
}
}
private fun addToRunningQueue() {
val uri = queue.poll()!!
runningQueue.add(uri)
uploadImageToCloudStorage(uri)
}
private fun uploadImageToCloudStorage(locationUri: Uri) {
val sRef: StorageReference = FirebaseStorage.getInstance().getReference(getLocation(locationUri))
sRef.putFile(locationUri)
.addOnSuccessListener { taskSnapshot ->
taskSnapshot.metadata!!.reference!!.downloadUrl
.addOnSuccessListener { uri ->
successList.add(uri)
runningQueue.remove(locationUri)
}
}
.addOnFailureListener {
failureList.add(locationUri)
runningQueue.remove(locationUri)
}
}
}
由于您的需要需要使用线程,所以为了防止竞争条件,我不得不使用 Collections.synchronizedList
。要使用它,您需要在 activity 中实现 QueueSyncCallback 并将其作为对 QueueSync 的引用传递。如果要以任何方式访问视图,请确保 completed
中编写的任何代码都包含在 runOnMainThread
中,因为 completed
不会 运行 在主线程上我所知。这应该可行,但我无法对其进行测试,因为它基于您当前的代码。
编辑:-编辑后回复
override fun completed(successList: MutableList<Uri>, failureList: MutableList<Uri>) {
imageUploadSuccess(successList)
hideProgressDialog()
}
override fun getFileExtension(uri: Uri): String {
Constants.getFileExtension(this, imageFileURI[i])
}