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 儲存分區根的資料。您的引用必須指向子 URL。
從記憶體中的資料上傳
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()
方法來取得下載檔案的 URL:
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 | 每當上傳失敗時都會呼叫此偵聽器。這可能是由於網路逾時、授權失敗或您取消任務而發生的。 |
使用Exception
實例呼叫OnFailureListener
。其他偵聽器使用UploadTask.TaskSnapshot
物件進行呼叫。該物件是事件發生時任務的不可變視圖。 UploadTask.TaskSnapshot
包含以下屬性:
財產 | 類型 | 描述 |
---|---|---|
getDownloadUrl | String | 可用於下載物件的 URL。這是一個不可猜測的公共 URL,可以與其他客戶端共用。上傳完成後就會填入該值。 |
getError | Exception | 如果任務失敗,則會將原因視為異常。 |
getBytesTransferred | long | 拍攝此快照時已傳輸的位元組總數。 |
getTotalByteCount | long | 預計上傳的總位元組數。 |
getUploadSessionUri | String | 可用於透過對 putFile 的另一次呼叫來繼續此任務的 URI。 |
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 重新啟動時使用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下載它們。