Как получить URL для загрузки нескольких изображений, загруженных с помощью Android и Firebase - PullRequest
0 голосов
/ 15 февраля 2020

Я вижу похожие вопросы на этом сайте, но большинство вопросов связано с получением URL для загрузки одного загруженного изображения. Пользуясь помощью этих сообщений, теперь я могу получить URL-адрес для загрузки одного изображения.

Но я сталкиваюсь с проблемой, когда пытаюсь получить URL-адрес для загрузки нескольких изображений, загруженных вместе. Я хочу сделать три вещи ...

1. Выберите три изображения
2. Загрузите их в Firebase Cloud Storage
3. Получите URL-адреса загруженных изображений и сохраните их в ArrayList.

Я могу сделать первые два Вещи успешно, но не удалось добиться третьего. Когда я нажимаю кнопку «обновить», все изображения прекрасно сохраняются в облачном хранилище, но при запросе 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

Это будет очень полезно для меня если кто-нибудь скажет мне исправление. Спасибо

1 Ответ

0 голосов
/ 15 февраля 2020

Ваш код страдает от использования комбинации локальных переменных и общих глобальных переменных при работе с асинхронным кодом и для циклов.

В приведенном выше коде вы используете глобальные переменные imagePath, imageAddress и imageList внутри a для l oop, что в конечном итоге является основной причиной этого исключения.

Разбивка кода

Когда вы нажимаете кнопку upload, ваш код выполняет следующие шаги с ошибки, выделенные жирным шрифтом:

  • Получает URI первого изображения
  • Обновляет значение imagePath, чтобы оно указывало на местоположение загрузки этого изображения
  • Начинает загрузку первое изображение
  • Журналы "NOT HELLO 0"
  • Получает URI второго изображения
  • Обновляет значение imagePath, чтобы оно указывало на местоположение загрузки этого изображения
  • Запускает загрузку второго изображения
  • Журналы "NOT HELLO 1"
  • Получает URI третьего изображения
  • Обновляет значение imagePath, чтобы указать место загрузки этого изображения
  • Начинается e загрузка третьего изображения
  • Журналы "HELLO HELLO" и тосты "Successfully Uploaded" (еще не закончено)
  • [несколько секунд спустя]
  • Первое изображение завершает загрузку
  • URL загрузки третьего изображения запрашивается (который выбрасывает StorageException)
  • Второе изображение завершает загрузку
  • Запрашивается URL-адрес загрузки третьего изображения (который выбрасывает другое StorageException)
  • Третье изображение завершает загрузку
  • Запрашивается URL загрузки третьего изображения (и он будет работать правильно)

Исправления

Чтобы это исправить, необходимо выполнить следующие действия: сделано:

  • Использовать локальную переменную копию imageList
  • Использовать локальную переменную для storageReference
  • Использовать локальную переменную для imagePath, и переименуйте в imageRef, чтобы точно отразить его тип
  • переименуйте imageAddress в imageAddressList, чтобы точно отразить его тип (рекомендуется)
  • Удалите while() l oop и использовать вместо него итератор
  • Отключить кнопку загрузки сразу, а не в конце
  • Загружать каждое изображение и получать URL-адреса загрузки параллельно, не конфликтуя друг с другом
  • Отображать сообщения «Успешно загружено» или «Ошибка загрузки» отображаются только после того, как загрузка фактически завершена
  • Обновлять imageAddressList только один раз, а не асинхронно.

Необходимо выполнить:

  • Обработка изменения жизненного цикла активности
  • Нажмите на currentUploadTask и привяжите его к диалоговому окну просмотра / уведомлению, чтобы показать ход загрузки файла
  • Обновление пользовательского интерфейса после завершения загрузки

Обновленный код

Примечание: это было напечатано от руки - ожидайте несколько опечаток.

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
                }
            });
    }
});
...