Cloud Storage for Firebase를 사용하면 Firebase에서 제공하고 관리하는 Cloud Storage 버킷에 파일을 빠르고 손쉽게 업로드할 수 있습니다.
파일 업로드
Cloud Storage에 파일을 업로드하려면 우선 파일 이름을 포함하여 파일의 전체 경로를 가리키는 참조를 만듭니다.
Kotlin
// 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
// 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
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
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. // ... } });
다운로드 URL 가져오기
파일을 업로드한 후 StorageReference
에서 getDownloadUrl()
메서드를 호출하면 파일 다운로드 URL을 가져올 수 있습니다.
Kotlin
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
// 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
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 |
객체를 다운로드하는 데 사용할 수 있는 URL입니다. 다른 클라이언트와 공유할 수 있는 추측 불가능한 공개 URL로서 업로드가 완료되면 값이 채워집니다. |
getError |
Exception |
작업이 실패한 경우 그 원인을 Exception으로 처리합니다. |
getBytesTransferred |
long |
이 스냅샷을 생성한 시점까지 전송된 총 바이트 수입니다. |
getTotalByteCount |
long |
업로드가 예정된 총 바이트 수입니다. |
getUploadSessionUri |
String |
putFile을 다시 호출하여 이 작업을 계속하는 데 사용할 수 있는 URI입니다. |
getMetadata |
StorageMetadata |
업로드가 완료되기 전에는 서버로 전송되는 메타데이터입니다. 업로드가 완료된 후에는 서버가 반환한 메타데이터입니다. |
getTask |
UploadTask |
이 스냅샷을 생성한 작업입니다. 이 작업을 사용하여 업로드를 취소, 일시중지 또는 재개할 수 있습니다. |
getStorage |
StorageReference |
UploadTask 를 만드는 데 사용된 StorageReference 입니다. |
UploadTask
이벤트 리스너로 업로드 이벤트를 간편하고 효과적인 방법으로 모니터링할 수 있습니다.
Kotlin
// 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"); } });
활동 수명 주기 변화 처리
대화상자 표시, 화면 회전 등으로 인해 활동 수명 주기가 달라져도 업로드는 백그라운드에서 계속됩니다. 연결된 리스너도 연결된 채로 유지됩니다. 이 때문에 활동이 중지된 후 리스너가 호출되면 예기치 않은 결과가 나타날 수 있습니다.
활동 범위와 함께 리스너를 구독하면 활동이 중지될 때 리스너가 자동으로 등록 취소되므로 문제가 해결됩니다. 그런 다음 활동이 다시 시작될 때 getActiveUploadTasks
메서드를 사용하여 여전히 실행 중이거나 최근에 완료된 업로드 작업을 가져올 수 있습니다.
아래 예시에서는 이 방법과 함께 사용된 스토리지 참조 경로를 유지하는 방법을 보여 줍니다.
Kotlin
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
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
// 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);
세션은 1주일 동안 지속됩니다. 만료되었거나 오류가 발생한 세션을 재개하려고 하면 실패 콜백이 수신됩니다. 개발자는 업로드 중간에 파일이 변경되지 않도록 해야 합니다.
오류 처리
업로드 시 로컬 파일이 없는 경우, 사용자에게 원하는 파일을 업로드할 권한이 없는 경우 등 다양한 이유로 오류가 발생할 수 있습니다. 오류의 자세한 내용은 문서의 오류 처리 섹션을 참조하세요.
전체 예시
다음은 진행률 모니터링 및 오류 처리를 포함하는 업로드를 보여주는 예시입니다.
Kotlin
// 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에서 파일을 다운로드하는 방법을 알아보세요.