繁体   English   中英

如何获取使用 Android 和 Firebase 上传的多张图片的下载 URL

[英]How to get download URL of multiple images uploaded using Android & Firebase

我在这个网站上看到了类似的问题,但大多数问题都与获取单个上传图像的下载 URL 相关。 从这些帖子中获得帮助,现在我可以获得单个图像的下载 URL。

但是当我尝试获取一起上传的多个图像的下载 URL 时,我遇到了一个问题。 我想做三件事...

1. 选择三张图片
2. 将它们上传到 Firebase 云存储
3. 获取上传图片的 URL 并保存在ArrayList

我可以成功地完成前两件事,但没有设法完成第三件事。 当我单击“更新”按钮时,所有图像都完美地存储在 Cloud Storage 中,但在请求所有图像的下载 URL 时显示错误。

这是我单击“更新”按钮时的代码:

upload.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        progressDialog.setMessage("Uploading .... ");
        progressDialog.show();

        storageReference = FirebaseStorage.getInstance().getReference().child("Pictures");

        int uploadCount = 0;

        // imageList is an ArrayList<Uri> which holds the address of selected 3 images. 
        // imageAddress is an ArrayList<String> where I want to save all downloadUrls of images (each url is saved as a string).
        // imagePath is a StorageReference

        while(uploadCount < imageList.size()) {
            Log.d("UploadCount", uploadCount+"");
            Uri uri_Image = imageList.get(uploadCount); 
            imagePath = storageReference.child(uri_Image.getLastPathSegment());

            imagePath.putFile(uri_Image).addOnSuccessListener(new OnSuccessListener<UploadTask.TaskSnapshot>() {
                @Override
                public void onSuccess(UploadTask.TaskSnapshot taskSnapshot) {
                    imagePath.getDownloadUrl().addOnSuccessListener(newOnSuccessListener<Uri>() {
                        @Override
                        public void onSuccess(Uri uri) {
                            Uri downloadUri = uri;
                            imageAddress.add(downloadUri.toString());
                            Log.d("ImageAddress Size: ", imageAddress.size()+"");
                        }
                    });
                }
            }).addOnFailureListener(new OnFailureListener() {
                @Override
                public void onFailure(@NonNull Exception e) {
                    Toast.makeText(SignOutActivity.this, e.getMessage(), Toast.LENGTH_LONG).show();
                    progressDialog.dismiss();
                }
            }); //.............
            if(uploadCount == (imageList.size()-1)) {
                Log.d("Good", "HELLO HELLO");
                Toast.makeText(SignOutActivity.this, "Successfully Uploaded", Toast.LENGTH_LONG).show();
                upload.setClickable(false);
                progressDialog.dismiss();
            }
            else {
                Log.d("BAD", "NOT HELLO "+uploadCount);
            }
            uploadCount = uploadCount + 1;

        }
    }
});

这是错误:

2020-02-15 17:02:26.945 28207-28735/com.example.practiceapplication E/StorageException: StorageException has occurred.
    Object does not exist at location.
    Code: -13010 HttpResult: 404
2020-02-15 17:02:26.946 28207-28735/com.example.practiceapplication E/StorageException: {"error": {"code": 404, "message": "Not Found. Could not get object", "status": "GET_OBJECT"}}
    java.io.IOException: {"error": {"code": 404, "message": "Not Found. Could not get object", "status": "GET_OBJECT"}}
        at com.google.firebase.storage.network.NetworkRequest.parseResponse(com.google.firebase:firebase-storage@@19.1.1:433)
        at com.google.firebase.storage.network.NetworkRequest.parseErrorResponse(com.google.firebase:firebase-storage@@19.1.1:450)
        at com.google.firebase.storage.network.NetworkRequest.processResponseStream(com.google.firebase:firebase-storage@@19.1.1:441)
        at com.google.firebase.storage.network.NetworkRequest.performRequest(com.google.firebase:firebase-storage@@19.1.1:272)
        at com.google.firebase.storage.network.NetworkRequest.performRequest(com.google.firebase:firebase-storage@@19.1.1:286)
        at com.google.firebase.storage.internal.ExponentialBackoffSender.sendWithExponentialBackoff(com.google.firebase:firebase-storage@@19.1.1:70)
        at com.google.firebase.storage.internal.ExponentialBackoffSender.sendWithExponentialBackoff(com.google.firebase:firebase-storage@@19.1.1:62)
        at com.google.firebase.storage.GetDownloadUrlTask.run(com.google.firebase:firebase-storage@@19.1.1:76)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1133)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:607)
        at java.lang.Thread.run(Thread.java:761)
2020-02-15 17:02:30.712 28207-28207/com.example.practiceapplication D/ImageAddress Size:: 1

如果有人告诉我更正,这对我会很有帮助。 谢谢

在处理异步代码和 for 循环时,您的代码会因混合使用局部变量和共享全局变量而受到影响。

在上面的代码中,您在 for 循环中使用全局变量imagePathimageAddressimageList ,这最终是该异常的关键原因。

代码分解

当您单击upload按钮时,您的代码将执行以下步骤,错误以粗体显示:

  • 获取第一张图片的 URI
  • 更新imagePath的值以指向该图像的上传位置
  • 开始上传第一张图片
  • 记录"NOT HELLO 0"
  • 获取第二张图片的 URI
  • 更新imagePath的值以指向该图像的上传位置
  • 开始上传第二张图片
  • 记录"NOT HELLO 1"
  • 获取第三张图片的 URI
  • 更新imagePath的值以指向该图像的上传位置
  • 开始上传第三张图片
  • 记录"HELLO HELLO"和 Toasts "Successfully Uploaded" (尚未实际完成)
  • [几分钟后]
  • 第一张图片上传完成
  • 请求第三张图片的下载 URL (抛出StorageException
  • 第二张图上传完成
  • 请求第三张图片的下载 URL (抛出另一个StorageException
  • 第三张图片上传完成
  • 请求第三张图片的下载 URL(并且可以正常工作)

修复

要解决此问题,必须执行以下操作:

  • 使用imageList的局部变量副本
  • 使用局部变量作为storageReference
  • imagePath使用局部变量,并重命名为imageRef以准确反映其类型
  • imageAddress重命名为imageAddressList以准确反映其类型(推荐)
  • 删除while()循环并使用 for 迭代器代替
  • 立即禁用上传按钮而不是最后
  • 上传每张图片并并行获取下载地址,互不冲突
  • 仅在实际完成上传后才显示“成功上传”或“上传失败”消息
  • 仅更新imageAddressList一次,而不是异步更新。

要做的:

  • 处理活动生命周期变化
  • 点击currentUploadTask并将其绑定到视图对话框/通知以显示文件上传进度
  • 完成所有上传后更新 UI

更新代码

注意:这是徒手输入的 - 预计会有一些错别字。

upload.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        progressDialog.setMessage("Uploading .... ");
        progressDialog.show();
        upload.setClickable(false); // disable upload button whilst uploading

        final StorageReference storageReference = FirebaseStorage.getInstance().getReference().child("Pictures");
        final List<Uri> clonedImageList = new ArrayList<>(imageList);

        imageList.clear(); // empty old list?
        int imageListSize = clonedImageList.size();

        List<Task<Uri>> uploadedImageUrlTasks = new ArrayList<>(imageListSize);

        for (Uri imageUri : clonedImageList) {
            final String imageFilename = imageUri.getLastPathSegment();
            Log.d("upload.onClick()", "Starting upload for \"" + imageFilename + "\"...");

            StorageReference imageRef = storageReference.child(imageFilename); // Warning: potential for collisions/overwrite
            UploadTask currentUploadTask = imageRef.putFile(imageUri);

            Task<Uri> currentUrlTask = currentUploadTask
                .continueWithTask(new Continuation<UploadTask.TaskSnapshot, Task<Uri>>() {
                    @Override
                    public Task<Uri> then(@NonNull Task<UploadTask.TaskSnapshot> task) throws Exception {
                        if (!task.isSuccessful()) {
                            Log.d("upload.onClick()", "Upload for \"" + imageFilename + "\" failed!");
                            throw task.getException(); // rethrow any errors
                        }

                        Log.d("upload.onClick()", "Upload for \"" + imageFilename + "\" finished. Fetching download URL...");
                        return imageRef.getDownloadUrl();
                    }
                })
                .continueWithTask(new Continuation<Uri, Uri>() { // purely for logging to debug, recommended to remove
                    @Override
                    public Task<Uri> then(@NonNull Task<Uri> task) throws Exception {
                        if (!task.isSuccessful()) {
                            Log.d("upload.onClick()", "Could not get download URL for \"" + imageFilename + "\"!");
                            throw task.getException(); // rethrow any errors
                        }

                        Log.d("upload.onClick()", "Download URL for \"" + imageFilename + "\" is \"" + task.getResult() + "\".");
                        return task.getResult();
                    }
                });

              uploadedImageUrlTasks.add(currentUrlTask);
        }

        // At this point, all the files are being uploaded in parallel
        // Each upload is tracked by the tasks in uploadedImageUrlTasks

        Tasks.whenAllComplete(uploadedImageUrlTasks)
            .addOnCompleteListener(new OnCompleteListener<List<Task<Uri>>>() {
                @Override
                public void onComplete(@NonNull List<Task<Uri>> tasks) {
                    int tasksCount = tasks.size();
                    List<Uri> failedUploads = new ArrayList<>();
                    imageAddressList.clear(); // empty old entries?

                    for (Task<Uri> task : tasks) {
                        if (task.isSuccessful()) {
                            successCount++;
                            Uri downloadUri = task.getResult();
                            imageAddressList.add(downloadUri.toString());
                        } else {
                            Uri imageUri = clonedImageList.get(tasks.indexOf(task));
                            failedUploads.add(imageUri);
                            Log.e("upload.onClick()", "Failed to upload/fetch URL for \"" + imageUri.getLastPathSegment() + "\" with exception", task.getException()); // log exception
                        }
                    }

                    progressDialog.dismiss(); // dismiss upload dialog

                    if (failedUploads.size() > 0) {
                        Toast.makeText(SignOutActivity.this, failedUploads.size() + "/" + tasksCount + " uploads failed.", Toast.LENGTH_LONG).show();
                        // TODO: Do something with list of failed uploads such as readd to the now empty upload list
                        imageList.addAll(failedUploads);
                        upload.setClickable(true);
                    } else {
                        Toast.makeText(SignOutActivity.this, "Successfully uploaded all " + tasksCount + " files.", Toast.LENGTH_LONG).show();
                    }

                    // TODO: Now that imageAddressList has been updated, update the UI - e.g tell recycler view to refresh
                }
            });
    }
});

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM