Cloud Storage for Firebase 可让您快速轻松地将文件上传到 Firebase 提供和管理的 Cloud Storage 存储桶中。
上传文件
如需将文件上传到 Cloud Storage,首先要创建对文件的完整路径(包括文件名)的引用。
Kotlin+KTX
// Create a storage reference from our app val storageRef = storage.reference // Create a reference to "mountains.jpg" val mountainsRef = storageRef.child("mountains.jpg") // Create a reference to 'images/mountains.jpg' val mountainImagesRef = storageRef.child("images/mountains.jpg") // While the file names are the same, the references point to different files mountainsRef.name == mountainImagesRef.name // true mountainsRef.path == mountainImagesRef.path // false
Java
// Create a storage reference from our app StorageReference storageRef = storage.getReference(); // Create a reference to "mountains.jpg" StorageReference mountainsRef = storageRef.child("mountains.jpg"); // Create a reference to 'images/mountains.jpg' StorageReference mountainImagesRef = storageRef.child("images/mountains.jpg"); // While the file names are the same, the references point to different files mountainsRef.getName().equals(mountainImagesRef.getName()); // true mountainsRef.getPath().equals(mountainImagesRef.getPath()); // false
创建适当的引用后,您可以调用 putBytes()
、putFile()
或 putStream()
方法将文件上传到 Cloud Storage。
您无法通过指向 Cloud Storage 存储桶根目录的引用来上传数据。您的引用必须指向一个子网址。
通过内存中的数据上传
putBytes()
方法是将文件上传到 Cloud Storage 最简单的方法。putBytes()
接受一个 byte[]
并返回 UploadTask
,您可以用它来管理和监控上传状态。
Kotlin+KTX
// Get the data from an ImageView as bytes imageView.isDrawingCacheEnabled = true imageView.buildDrawingCache() val bitmap = (imageView.drawable as BitmapDrawable).bitmap val baos = ByteArrayOutputStream() bitmap.compress(Bitmap.CompressFormat.JPEG, 100, baos) val data = baos.toByteArray() var uploadTask = mountainsRef.putBytes(data) uploadTask.addOnFailureListener { // Handle unsuccessful uploads }.addOnSuccessListener { taskSnapshot -> // taskSnapshot.metadata contains file metadata such as size, content-type, etc. // ... }
Java
// Get the data from an ImageView as bytes imageView.setDrawingCacheEnabled(true); imageView.buildDrawingCache(); Bitmap bitmap = ((BitmapDrawable) imageView.getDrawable()).getBitmap(); ByteArrayOutputStream baos = new ByteArrayOutputStream(); bitmap.compress(Bitmap.CompressFormat.JPEG, 100, baos); byte[] data = baos.toByteArray(); UploadTask uploadTask = mountainsRef.putBytes(data); uploadTask.addOnFailureListener(new OnFailureListener() { @Override public void onFailure(@NonNull Exception exception) { // Handle unsuccessful uploads } }).addOnSuccessListener(new OnSuccessListener<UploadTask.TaskSnapshot>() { @Override public void onSuccess(UploadTask.TaskSnapshot taskSnapshot) { // taskSnapshot.getMetadata() contains file metadata such as size, content-type, etc. // ... } });
由于 putBytes()
接受的是 byte[]
,因此它需要您的应用一次将文件的全部内容保存在内存中。请考虑使用 putStream()
或 putFile()
以减少使用的内存。
通过数据流上传
putStream()
方法是将文件上传到 Cloud Storage 最简单的方法。putStream()
接受一个 InputStream
并返回 UploadTask
,您可以用它来管理和监控上传状态。
Kotlin+KTX
val stream = FileInputStream(File("path/to/images/rivers.jpg")) uploadTask = mountainsRef.putStream(stream) uploadTask.addOnFailureListener { // Handle unsuccessful uploads }.addOnSuccessListener { taskSnapshot -> // taskSnapshot.metadata contains file metadata such as size, content-type, etc. // ... }
Java
InputStream stream = new FileInputStream(new File("path/to/images/rivers.jpg")); uploadTask = mountainsRef.putStream(stream); uploadTask.addOnFailureListener(new OnFailureListener() { @Override public void onFailure(@NonNull Exception exception) { // Handle unsuccessful uploads } }).addOnSuccessListener(new OnSuccessListener<UploadTask.TaskSnapshot>() { @Override public void onSuccess(UploadTask.TaskSnapshot taskSnapshot) { // taskSnapshot.getMetadata() contains file metadata such as size, content-type, etc. // ... } });
通过本地文件上传
您可以使用 putFile()
方法上传设备上的本地文件,例如相机中的照片和视频。putFile()
接受一个 File
并会返回 UploadTask
,您可以用它来管理和监控上传状态。
Kotlin+KTX
var file = Uri.fromFile(File("path/to/images/rivers.jpg")) val riversRef = storageRef.child("images/${file.lastPathSegment}") uploadTask = riversRef.putFile(file) // Register observers to listen for when the download is done or if it fails uploadTask.addOnFailureListener { // Handle unsuccessful uploads }.addOnSuccessListener { taskSnapshot -> // taskSnapshot.metadata contains file metadata such as size, content-type, etc. // ... }
Java
Uri file = Uri.fromFile(new File("path/to/images/rivers.jpg")); StorageReference riversRef = storageRef.child("images/"+file.getLastPathSegment()); uploadTask = riversRef.putFile(file); // Register observers to listen for when the download is done or if it fails uploadTask.addOnFailureListener(new OnFailureListener() { @Override public void onFailure(@NonNull Exception exception) { // Handle unsuccessful uploads } }).addOnSuccessListener(new OnSuccessListener<UploadTask.TaskSnapshot>() { @Override public void onSuccess(UploadTask.TaskSnapshot taskSnapshot) { // taskSnapshot.getMetadata() contains file metadata such as size, content-type, etc. // ... } });
获取下载网址
上传文件后,您可以调用 StorageReference
的 getDownloadUrl()
方法,获取下载文件的网址:
Kotlin+KTX
val ref = storageRef.child("images/mountains.jpg") uploadTask = ref.putFile(file) val urlTask = uploadTask.continueWithTask { task -> if (!task.isSuccessful) { task.exception?.let { throw it } } ref.downloadUrl }.addOnCompleteListener { task -> if (task.isSuccessful) { val downloadUri = task.result } else { // Handle failures // ... } }
Java
final StorageReference ref = storageRef.child("images/mountains.jpg"); uploadTask = ref.putFile(file); 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 ref.getDownloadUrl(); } }).addOnCompleteListener(new OnCompleteListener<Uri>() { @Override public void onComplete(@NonNull Task<Uri> task) { if (task.isSuccessful()) { Uri downloadUri = task.getResult(); } else { // Handle failures // ... } } });
添加文件元数据
在上传文件时,您还可以包含元数据。这些元数据包含典型的文件元数据属性,如 name
、size
和 contentType
(通常称为 MIME 类型)。putFile()
方法会自动根据 File
扩展名推断 MIME 类型,您也可以通过在元数据中指定 contentType
来覆盖自动检测到的类型。如果您未提供 contentType
,并且 Cloud Storage 无法根据文件扩展名推断出默认值,则 Cloud Storage 将使用 application/octet-stream
。如需详细了解文件元数据,请参阅使用文件元数据部分。
Kotlin+KTX
// Create file metadata including the content type var metadata = storageMetadata { contentType = "image/jpg" } // Upload the file and metadata uploadTask = storageRef.child("images/mountains.jpg").putFile(file, metadata)
Java
// Create file metadata including the content type StorageMetadata metadata = new StorageMetadata.Builder() .setContentType("image/jpg") .build(); // Upload the file and metadata uploadTask = storageRef.child("images/mountains.jpg").putFile(file, metadata);
管理上传
除了启动上传外,您还可以分别使用 pause()
、resume()
和 cancel()
方法来暂停、恢复和取消上传。暂停和恢复事件会分别导致状态变为 pause
和 progress
。取消上传会导致上传失败,并显示一条错误指明上传已被取消。
Kotlin+KTX
uploadTask = storageRef.child("images/mountains.jpg").putFile(file) // Pause the upload uploadTask.pause() // Resume the upload uploadTask.resume() // Cancel the upload uploadTask.cancel()
Java
uploadTask = storageRef.child("images/mountains.jpg").putFile(file); // Pause the upload uploadTask.pause(); // Resume the upload uploadTask.resume(); // Cancel the upload uploadTask.cancel();
监控上传进度
您可以添加监听器来处理上传任务中的成功、失败、进度变更或暂停事件:
监听器类型 | 典型用法 |
---|---|
OnProgressListener |
此监听器在数据传输期间定期调用,可用于为上传/下载进度指示器提供数据。 |
OnPausedListener |
每次任务暂停时都会调用此监听器。 |
OnSuccessListener |
当任务成功完成时会调用此监听器。 |
OnFailureListener |
每次上传失败时都会调用此监听器。网络超时、授权失败或取消任务可能会导致上传失败。 |
OnFailureListener
是通过 Exception
实例调用的。其他监听器则是通过 UploadTask.TaskSnapshot
对象调用的。此对象是事件发生时的任务视图,该视图不可改变。UploadTask.TaskSnapshot
包含以下属性:
属性 | 类型 | 说明 |
---|---|---|
getDownloadUrl |
String |
可用于下载对象的网址。这是一个无法猜测的公开网址,可以与其他客户端共享。上传完成后,将会填充此值。 |
getError |
Exception |
如果任务失败,此属性将会获得“Exception”类型的原因。 |
getBytesTransferred |
long |
截取此快照时已传输的总字节数。 |
getTotalByteCount |
long |
预期应上传的总字节数。 |
getUploadSessionUri |
String |
该 URI 可用于通过再次调用 putFile 来继续此任务。 |
getMetadata |
StorageMetadata |
在上传完成之前,这是发送到服务器的元数据。上传完成后,这是服务器返回的元数据。 |
getTask |
UploadTask |
创建此快照的任务。使用此任务可以取消、暂停或恢复上传。 |
getStorage |
StorageReference |
用于创建 UploadTask 的 StorageReference 。 |
UploadTask
事件监听器提供了一种简单而强大的方法来监控上传事件。
Kotlin+KTX
// Observe state change events such as progress, pause, and resume // You'll need to import com.google.firebase.storage.component1 and // com.google.firebase.storage.component2 uploadTask.addOnProgressListener { (bytesTransferred, totalByteCount) -> val progress = (100.0 * bytesTransferred) / totalByteCount Log.d(TAG, "Upload is $progress% done") }.addOnPausedListener { Log.d(TAG, "Upload is paused") }
Java
// Observe state change events such as progress, pause, and resume uploadTask.addOnProgressListener(new OnProgressListener<UploadTask.TaskSnapshot>() { @Override public void onProgress(UploadTask.TaskSnapshot taskSnapshot) { double progress = (100.0 * taskSnapshot.getBytesTransferred()) / taskSnapshot.getTotalByteCount(); Log.d(TAG, "Upload is " + progress + "% done"); } }).addOnPausedListener(new OnPausedListener<UploadTask.TaskSnapshot>() { @Override public void onPaused(UploadTask.TaskSnapshot taskSnapshot) { Log.d(TAG, "Upload is paused"); } });
处理 activity 生命周期变更
即使 activity 生命周期发生变化(例如显示对话框或旋转屏幕),上传也将继续在后台进行。您连接的所有监听器也都会保持连接状态。如果在 activity 停止后调用这些监听器,有可能产生意外的结果。
您可以采取以下方法来解决此问题:向 activity 作用域订阅您的监听器,以便在 activity 停止时自动取消注册。然后,在 activity 重新启动后,使用 getActiveUploadTasks
方法获取仍在进行或刚刚完成的上传任务。
下面的示例演示了这一操作,还说明了如何保留使用的存储引用路径。
Kotlin+KTX
override fun onSaveInstanceState(outState: Bundle) { super.onSaveInstanceState(outState) // If there's an upload in progress, save the reference so you can query it later outState.putString("reference", storageRef.toString()) } override fun onRestoreInstanceState(savedInstanceState: Bundle) { super.onRestoreInstanceState(savedInstanceState) // If there was an upload in progress, get its reference and create a new StorageReference val stringRef = savedInstanceState.getString("reference") ?: return storageRef = Firebase.storage.getReferenceFromUrl(stringRef) // Find all UploadTasks under this StorageReference (in this example, there should be one) val tasks = storageRef.activeUploadTasks if (tasks.size > 0) { // Get the task monitoring the upload val task = tasks[0] // Add new listeners to the task using an Activity scope task.addOnSuccessListener(this) { // Success! // ... } } }
Java
@Override protected void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); // If there's an upload in progress, save the reference so you can query it later if (mStorageRef != null) { outState.putString("reference", mStorageRef.toString()); } } @Override protected void onRestoreInstanceState(Bundle savedInstanceState) { super.onRestoreInstanceState(savedInstanceState); // If there was an upload in progress, get its reference and create a new StorageReference final String stringRef = savedInstanceState.getString("reference"); if (stringRef == null) { return; } mStorageRef = FirebaseStorage.getInstance().getReferenceFromUrl(stringRef); // Find all UploadTasks under this StorageReference (in this example, there should be one) List<UploadTask> tasks = mStorageRef.getActiveUploadTasks(); if (tasks.size() > 0) { // Get the task monitoring the upload UploadTask task = tasks.get(0); // Add new listeners to the task using an Activity scope task.addOnSuccessListener(this, new OnSuccessListener<UploadTask.TaskSnapshot>() { @Override public void onSuccess(UploadTask.TaskSnapshot state) { // Success! // ... } }); } }
getActiveUploadTasks
在提供的引用路径及其子路径中检索所有活跃的上传任务,因此您可能需要处理多个任务。
在进程重启后继续上传
如果您的进程关闭,所有正在进行的上传都将中断。但是,您可以在进程重新启动后恢复与服务器的上传会话来继续上传。这样就无需从文件开头部分上传,可以节省时间和带宽。
为此,请通过 putFile
开始上传。在生成的 StorageTask
中,调用 getUploadSessionUri
并将返回的值保存在永久性存储空间(如 SharedPreferences)中。
Kotlin+KTX
uploadTask = storageRef.putFile(localFile) uploadTask.addOnProgressListener { taskSnapshot -> sessionUri = taskSnapshot.uploadSessionUri if (sessionUri != null && !saved) { saved = true // A persisted session has begun with the server. // Save this to persistent storage in case the process dies. } }
Java
uploadTask = mStorageRef.putFile(localFile); uploadTask.addOnProgressListener(new OnProgressListener<UploadTask.TaskSnapshot>() { @Override public void onProgress(UploadTask.TaskSnapshot taskSnapshot) { Uri sessionUri = taskSnapshot.getUploadSessionUri(); if (sessionUri != null && !mSaved) { mSaved = true; // A persisted session has begun with the server. // Save this to persistent storage in case the process dies. } } });
当您的进程因上传中断而重启后,再次调用 putFile,并传递已保存的 URI。
Kotlin+KTX
// resume the upload task from where it left off when the process died. // to do this, pass the sessionUri as the last parameter uploadTask = storageRef.putFile( localFile, storageMetadata { }, sessionUri, )
Java
//resume the upload task from where it left off when the process died. //to do this, pass the sessionUri as the last parameter uploadTask = mStorageRef.putFile(localFile, new StorageMetadata.Builder().build(), sessionUri);
会话将持续一周。如果您在会话过期或出现错误后尝试恢复会话,将会接收到失败回调。您应确保在两次上传期间文件未进行任何更改。
错误处理
导致上传时出现错误的原因有很多,包括本地文件不存在,或者用户不具备上传所需文件的权限。如需了解错误详情,请参阅相关文档的处理错误部分。
完整示例
下面是一个包含进度监控和错误处理的完整上传示例:
Kotlin+KTX
// File or Blob file = Uri.fromFile(File("path/to/mountains.jpg")) // Create the file metadata metadata = storageMetadata { contentType = "image/jpeg" } // Upload file and metadata to the path 'images/mountains.jpg' uploadTask = storageRef.child("images/${file.lastPathSegment}").putFile(file, metadata) // Listen for state changes, errors, and completion of the upload. // You'll need to import com.google.firebase.storage.component1 and // com.google.firebase.storage.component2 uploadTask.addOnProgressListener { (bytesTransferred, totalByteCount) -> val progress = (100.0 * bytesTransferred) / totalByteCount Log.d(TAG, "Upload is $progress% done") }.addOnPausedListener { Log.d(TAG, "Upload is paused") }.addOnFailureListener { // Handle unsuccessful uploads }.addOnSuccessListener { // Handle successful uploads on complete // ... }
Java
// File or Blob file = Uri.fromFile(new File("path/to/mountains.jpg")); // Create the file metadata metadata = new StorageMetadata.Builder() .setContentType("image/jpeg") .build(); // Upload file and metadata to the path 'images/mountains.jpg' uploadTask = storageRef.child("images/"+file.getLastPathSegment()).putFile(file, metadata); // Listen for state changes, errors, and completion of the upload. uploadTask.addOnProgressListener(new OnProgressListener<UploadTask.TaskSnapshot>() { @Override public void onProgress(UploadTask.TaskSnapshot taskSnapshot) { double progress = (100.0 * taskSnapshot.getBytesTransferred()) / taskSnapshot.getTotalByteCount(); Log.d(TAG, "Upload is " + progress + "% done"); } }).addOnPausedListener(new OnPausedListener<UploadTask.TaskSnapshot>() { @Override public void onPaused(UploadTask.TaskSnapshot taskSnapshot) { Log.d(TAG, "Upload is paused"); } }).addOnFailureListener(new OnFailureListener() { @Override public void onFailure(@NonNull Exception exception) { // Handle unsuccessful uploads } }).addOnSuccessListener(new OnSuccessListener<UploadTask.TaskSnapshot>() { @Override public void onSuccess(UploadTask.TaskSnapshot taskSnapshot) { // Handle successful uploads on complete // ... } });
现在您已经上传了文件,接下来,我们将学习如何从 Cloud Storage 下载文件。