上传文件到Firebase后如何获取文件的URL?

25
一旦您将文件上传至Firebase,如何获取其URL以便稍后使用?我想将URL写入Firebase数据库,以便其他用户可以访问该图像。
我是这样上传文件的:
public void uploadFile()
{

    StorageReference filepath = mstorageRef.child("folder").child(filename);

    Uri File= Uri.fromFile(new File(mFileName));

    filepath.putFile(File).addOnSuccessListener(new OnSuccessListener<UploadTask.TaskSnapshot>() {
        @Override
        public void onSuccess(UploadTask.TaskSnapshot taskSnapshot)
        {
            Toast.makeText(MtActivity.this, "Upload Done", Toast.LENGTH_LONG).show();
        }
    });
}

我已确认文件正在上传,现在我只需要URL,以便将其写入我的数据库。然而,当我尝试这样做时:

Uri downloadUrl = taskSnapshot.getMetadata().getDownloadUrl();

它给我一个错误并显示此方法仅应从测试或私有范围内访问

我不确定这是什么意思,也不知道为什么会出现这个错误,因为我正在遵循Firebase提供的此示例

是否有一种新的获取URL的方式?

另外,那个URL对于特定的项目是唯一的吗?也就是说,如果我将其存储到数据库中并尝试稍后访问它,我能够这样做吗?


你应该写 taskSnapshot.getDownloadUrl(),而不是 taskSnapshot.getMetadata().getDownloadUrl() - Anggrayudi H
@AnggrayudiH 我尝试了这个方法,但是却得到了相同的错误信息。由于某种原因,它就是不允许我在addOnSuccessListener()方法内调用该方法。 - Isabel Alphonse
关于您看到的错误,您是否看过这篇文章 - AL.
请查看此解决方案 https://dev59.com/_Kvka4cB1Zd3GeqPnxnB 它对我有效。 - uche Godfrey
13个回答

42

您正在使用已被弃用的UploadTask.getDownloadUrl()。您可以使用StorageReference.getDownloadUrl()

在您的情况下,您可以尝试此方法 -

 filepath.putFile(File).addOnSuccessListener(new OnSuccessListener<UploadTask.TaskSnapshot>() {
    @Override
    public void onSuccess(UploadTask.TaskSnapshot taskSnapshot)
    {
         filepath.getDownloadUrl().addOnSuccessListener(new OnSuccessListener<Uri>() {
                    @Override
                    public void onSuccess(Uri uri) {
                        Uri downloadUrl = uri;
                       //Do what you want with the url
                    }
        Toast.makeText(MtActivity.this, "Upload Done", Toast.LENGTH_LONG).show();
    }
});

注意,StorageReference.getDownloadUrl()返回的是一个异步任务(Task),必须进行异步处理。你不能使用 Uri downloadUrl = photoRef.getDownloadUrl().getResult(); 这样的同步代码,否则会得到 java.lang.IllegalStateException: Task is not yet complete 异常。


在 Uri downloadUrl = uri; 之后,可使用的 URL 字符串为 downloadUrl.toString(); - DragonFire
为什么需要在 getDownloadUrl() 中附加 addOnSuccessListener?由于上传文件的路径是一个非常小的字符串,我认为必须有一种方法可以从 ..putFile(...).addonSuccesslistener(. <即此处>.) 中接收到的 taskSnapshot 中检索路径。 - ansh sachdeva
@anshsachdeva 你可以访问https://firebase.google.com/docs/storage/android/upload-files#get_a_download_url 进一步了解。我认为这个链接也会对你有帮助- https://firebase.google.com/docs/reference/android/com/google/firebase/storage/UploadTask.TaskSnapshot.html如果你找到其他方法,请添加/更新答案。 - Ananth

2
这种方法也可以,而且更简单一些。虽然不是什么“惊人”的事情,但它可以将您的代码减少到几行。希望这个答案有所帮助。
StorageReference filepath = mstorageRef.child("folder").child(filename);
filepath.putFile(File).addOnSuccessListener(new OnSuccessListener<UploadTask.TaskSnapshot>() {
        @Override
        public void onSuccess(UploadTask.TaskSnapshot taskSnapshot){
            Uri downloadUrl = filepath.getDownloadUrl();  // here is Url for photo  
            Toast.makeText(MtActivity.this, "Upload Done", Toast.LENGTH_LONG).show();
        }
});

2

如上所述,StorageReference.getDownloadUrl()返回一个任务。

以下是我的代码:

        filepath.putFile(imageUri).addOnCompleteListener(new OnCompleteListener<UploadTask.TaskSnapshot>() {
            @Override
            public void onComplete(@NonNull Task<UploadTask.TaskSnapshot> task) {
                if(task.isSuccessful()){

                    filepath.getDownloadUrl().addOnSuccessListener(new OnSuccessListener<Uri>() {
                        @Override
                        public void onSuccess(Uri uri) {
                            Uri downloadUri = uri;
                            String download_url = uri.toString();
                            mUserDatabase.child("image").setValue(download_url).addOnCompleteListener(new OnCompleteListener<Void>() {
                              @Override

                                public void onComplete(@NonNull Task<Void> task) {

                                   if(task.isSuccessful()) {

                                          mProgressDialog.dismiss();
                                          Toast.makeText(SettingActivity.this, "Successfully uploaded", Toast.LENGTH_LONG).show();

                                   }else {
                                       Toast.makeText(SettingActivity.this, "Error happened during the upload process", Toast.LENGTH_LONG).show();
                                   }
                                }
                            });
                        }
                    });


                }else{
                    Toast.makeText(SettingActivity.this, "Error happened during the upload process", Toast.LENGTH_LONG ).show();
                }
            }
        });

1
Here is how I did it
1) 这是我的上传并获取HTTP URL代码:
UploadTask uploadTask = FirebaseStorage.getInstance().getReference().child("id").child("filename")
                                .putFile(uri);
                        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();
                                }

                                // Continue with the task to get the download URL
                                return FirebaseStorage.getInstance().getReference().child(user.getUid()).child(avatarName).getDownloadUrl();
                            }
                        }).addOnCompleteListener(new OnCompleteListener<Uri>() {
                            @Override
                            public void onComplete(@NonNull Task<Uri> task) {
                                if (task.isSuccessful()) {
                                    Uri downloadUri = task.getResult();
                                    FirebaseDatabase.getInstance().getReference().child(user.getUid())
                                            .child(avatarName)
                                            .child("avatar_image")
                                            .setValue(downloadUri.toString());
                                    Toast.makeText(getContext(), "Success", Toast.LENGTH_SHORT).show();
                                } else {
                                    // Handle failures
                                    // ...
                                    Toast.makeText(getContext(), "Failed", Toast.LENGTH_SHORT).show();
                                }
                            }
                        });

2)这是我从图库中选择图片后的onActivityResult代码:

@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);

    if (requestCode == PICK_IMAGE_REQUEST && resultCode == RESULT_OK && data != null && data.getData() != null) {

        uri = data.getData();

        try {
            bitmap = MediaStore.Images.Media.getBitmap(getContext().getContentResolver(), uri);
            // Log.d(TAG, String.valueOf(bitmap));
            ivAvatarPic.setImageBitmap(bitmap);
            //ImageView imageView = (ImageView) findViewById(R.id.imageView);
            //imageView.setImageBitmap(bitmap);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

0
使用以下代码
val imageGalleryRef = storageReference?.child(name + "_gallery")
            val uploadTask = imageGalleryRef.putFile(file)
            uploadTask.addOnFailureListener({ e ->
                Log.e(TAG, "onFailure sendFileFirebase " + e.message)
            }).addOnCompleteListener(
                    object : OnCompleteListener<UploadTask.TaskSnapshot> {
                        override fun onComplete(p0: Task<UploadTask.TaskSnapshot>) {
                            imageGalleryRef.downloadUrl.addOnSuccessListener { e ->
                                run {
                                     downloadUrl = e.toString() // e is image download Uri
                                }
                            }
                        }
                    }

            )

0
在Ionic 5 + capacitor中,这是我最终能够同时监视上传并获取完成的文件名的方法。
代码来自谷歌自己的文档和一些谷歌搜索(包括https://ionicthemes.com/tutorials/about/ionic-firebase-image-upload等)。
它使用画布(据我所知)从图像生成一个base64字符串。这个方法可行,而Ionic的File和Base64则不行。
在页面上,我有一个<ion-progressbar>显示百分比(this.downloadProgress),如果它>0。我还有一个<ion-img>,其中src=this.imageUrl,在上传图片时显示图片。
我无法让上面回答中的任何技术方法工作,甚至无法编译。但我不知道为什么。
另一个注意点是,一些示例代码使用function()而不是箭头语法

encodeImageUri(imageURI, function(image64) {

需要将其重写为箭头语法以绑定/传递“this”到调用的函数,如下所示:
encodeImageUri(imageURI, image64 => {
    uploadImage(imageURI) {
    console.log(`try upload ${imageURI}`)
      let storageRef = firebase.storage().ref();
      let imageRef = storageRef.child('image').child('imageName');
      this.encodeImageUri(imageURI, image64 => {

        // create upload task
        const uplTask = imageRef.putString(image64, 'data_url');

        uplTask.on('state_changed',
          snapshot => {
            var progress = (snapshot.bytesTransferred / snapshot.totalBytes);
            console.log('Upload is ' + progress * 100 + '% done'); // might display this somewhere
            this.downloadProgress = progress; // update progress bar
          },
          error => {
            console.log('upload error');
          },
          () => {
            uplTask.snapshot.ref.getDownloadURL()
              .then(downloadURL => {
                console.log('File available at', downloadURL);
                this.imageUrl = downloadURL;
                this.downloadProgress = -1; // negative values hide the progress bar
              });        
          }
        );
      });
  }

  encodeImageUri(imageUri, callback) {
    var c = document.createElement('canvas');
    var ctx = c.getContext("2d");
    var img = new Image();
    img.onload = function () {
      var aux:any = this;
      c.width = aux.width;
      c.height = aux.height;
      ctx.drawImage(img, 0, 0);
      var dataURL = c.toDataURL("image/jpeg");
      callback(dataURL);
    };
    img.src = imageUri;
  };

当然,这有点无聊,因为它总是在覆盖相同的Firebase存储文件 :) 但这是可以解决的。

0
  Future<List<String>> uploadImages(List<String> filepaths) async {
    List<String> uploadUrls = [];

    await Future.wait(filepaths.map((String filepath) async {
      FirebaseStorage storage = FirebaseStorage(storageBucket: 'gs://your-bucket-name.appspot.com');
      StorageUploadTask uploadTask = storage.ref().child("images/${basename(filepath)}").putFile(File(filepath));
      StorageTaskSnapshot storageTaskSnapshot;

      StorageTaskSnapshot snapshot = await uploadTask.onComplete;
      if (snapshot.error == null) {
        storageTaskSnapshot = snapshot;
        final String downloadUrl = await storageTaskSnapshot.ref.getDownloadURL();
        uploadUrls.add(downloadUrl);

        print('Upload success');
      } else {
        print('Error from image repo ${snapshot.error.toString()}');
        throw ('This file is not an image');
      }
    }), eagerError: true, cleanUp: (_) {
      print('eager cleaned up');
    });

    return uploadUrls;
  }
}

0

不确定这是否符合您的要求,因为我正在使用 Kotlin。这是在我的当前项目中对我有效的方法。希望它能帮助未来的某个人。

首先,我从相机或图库获取图像。我使用当前时间戳使文件名唯一。

fun uploadFileFromMemory() {

        showLoading()

        val currentTimestamp = System.currentTimeMillis()

        val storage = Firebase.storage("Your storage url")

        // Create a storage reference from our app
        val storageRef = storage.reference

        // Create a reference to "mountains.jpg"
        val mountainsRef = storageRef.child("images/picture$currentTimestamp.jpg")

        // Get the data from an adminImageView as bytes
        adminImageView.isDrawingCacheEnabled = true
        adminImageView.buildDrawingCache()
        val bitmap = (adminImageView.drawable as BitmapDrawable).bitmap
        val baos = ByteArrayOutputStream()
        bitmap.compress(Bitmap.CompressFormat.JPEG, 100, baos)
        val data = baos.toByteArray()

        var uploadTask = mountainsRef.putBytes(data)

        val urlTask = uploadTask.continueWithTask { task ->
            if (!task.isSuccessful) {
                task.exception?.let {
                    throw it
                }
            }
            mountainsRef.downloadUrl
        }.addOnCompleteListener { task ->
            if (task.isSuccessful) {
                val downloadUri = task.result

                updateServicePicture(downloadUri.toString())

            } else {
                hideLoading()

                showToast("Something went wrong")
            }
        }

接着,我会使用这个函数获取新更新文件的URL,并将其用于更新我的Firestore数据库中的图片URL属性。

fun updateServicePicture(url: String) {

        val db = FirebaseFirestore.getInstance()

        val id = arguments?.getString("id")

        if (id !== null) {

            val ref = db.collection("services").document(id)

// Set the "isCapital" field of the city 'DC'
            ref
                .update("picture", url)
                .addOnSuccessListener { Log.d(TAG, "DocumentSnapshot successfully updated!")

                    hideLoading()

                    showToast("Picture has been successfully updated")
                }
                .addOnFailureListener { e -> Log.w(TAG, "Error updating document", e)

                    hideLoading()

                    showToast("Something went wrong")
                }

        }
    }

hideLoading()、showloading() 和 showToast() 是我用于进度条和提示框的函数。 希望这能帮助到某些人。 通过文档和一些测试,最终我让它正常工作了。祝大家编程愉快。


0

试试这个:

 filepath.putFile(File).addOnCompleteListener(new OnCompleteListener<UploadTask.TaskSnapshot>() {
         @Override
         public void onComplete(@NonNull Task<UploadTask.TaskSnapshot> task) {
            if(task.isSuccessful()){
                 String fileUri = task.getResult().getUploadSessionUri().toString();
                         // Do whatever you want with fileUri 

                        }
                   }
            }) ;

0

URI更改为URL

val urlTask = uploadTask.continueWith { task ->
            if (!task.isSuccessful) {
                task.exception?.let {
                    throw it
                }
            }


            spcaeRef.downloadUrl
        }.addOnCompleteListener { task ->
            if (task.isSuccessful) {

                val downloadUri = task.result

                //URL
                val url = downloadUri!!.result

            } else {
                //handle failure here
            }
        }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接