【问题标题】:While loop doesn't seem to work with .putFile when uploading multiple images to Firebase storage in Kotlin将多个图像上传到 Kotlin 中的 Firebase 存储时,while 循环似乎不适用于 .putFile
【发布时间】:2021-09-21 03:41:04
【问题描述】:

我一直在尝试将多张图片上传到Firebase 存储。但是,我无法成功地做到这一点。我可以成功地将图像(单个)上传到存储并将图像的 URL 添加到Firestore,现在我修改了我的代码以上传最多五张图像,它可以是从 1 到 5 的任意数量的图像。

    R.id.btn_submit -> {
    if (validateDetails()) {
        uploadImage()
    }
}

上面的代码,在验证字段后调用下面的函数,然后调用函数uploadImageToCloudStoragemSelectedImageFileUriListprivate 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 文件并在答案中添加了代码。图片和按钮更改为的活动

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()

}

我在AddProductActivity类中也实现了这两个方法,但是不知道里面应该放什么。

    override fun completed(successList: MutableList<Uri>, failureList: MutableList<Uri>) {
    TODO("Not yet implemented")
}

override fun getFileExtension(uri: Uri): String {
    TODO("Not yet implemented")
}

错误:

【问题讨论】:

  • 有错误信息吗?如果有请编辑。
  • 也许尝试一个一个发送文件
  • 您在这里所做的是您的 while 循环正在遍历所有文件,并且在其中 Firebase 存储正在异步发送文件,这意味着在一个文件完全发送之前另一个文件开始上传。如果您有很长的文件列表,这种方式将无法维持。相反,您应该尝试使用递归解决方案来同步文件上传,因此一次只上传一个,或者您可以使用自定义队列一次上传五个或更多文件,然后尝试上传其余文件
  • 当然让我做点什么。
  • 尝试编辑后的答案

标签: kotlin firebase-storage


【解决方案1】:

这应该可行

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。要使用它,您需要在活动中实现 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])
}

【讨论】:

  • 如果可行,请点赞并为其他人接受此答案。
  • 当然,我会的。我不知道现在从哪里开始。让我检查如何使用代码。你能给我一个提示吗?我不是这方面的专家。
  • 只需将其添加为 kt 文件并制作 QueueSync 对象,然后调用 startUploading 即可
  • 好的,我正在尝试。当我调用startUploading 时,应该为参数callback 传递什么值。我就是这样做的。 QueueSync( mSelectedImageFileUriList, Constants.PRODUCT_IMAGE, Constants.PRODUCT_IMAGE_DIRECTORY_NAME, et_product_title.text.toString().trim { it &lt;= ' ' }, ).startUploading() 另外,“从未使用过属性'title`”。有什么遗漏吗?
  • 你必须在你的活动中实现QueueSyncCallback并发送this作为回调
猜你喜欢
  • 2019-11-26
  • 2022-11-11
  • 2020-03-28
  • 2021-09-18
  • 1970-01-01
  • 2019-04-07
  • 2021-10-14
  • 2022-12-05
  • 2017-09-25
相关资源
最近更新 更多