Android에서 Cloud Storage로 파일 다운로드

Firebase용 Cloud Storage를 사용하면 Firebase에서 제공하고 관리하는 Cloud Storage 버킷에서 파일을 빠르고 손쉽게 다운로드할 수 있습니다.

참조 만들기

파일을 다운로드하려면 우선 다운로드할 파일을 가리키는 Cloud Storage 참조를 만듭니다.

Cloud Storage 버킷의 루트에 하위 경로를 추가하여 참조를 만들거나 Cloud Storage의 객체를 참조하는 기존 gs:// 또는 https:// URL로 참조를 만들 수 있습니다.

Kotlin+KTX

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

// Create a reference with an initial file path and name
val pathReference = storageRef.child("images/stars.jpg")

// Create a reference to a file from a Google Cloud Storage URI
val gsReference = storage.getReferenceFromUrl("gs://bucket/images/stars.jpg")

// Create a reference from an HTTPS URL
// Note that in the URL, characters are URL escaped!
val httpsReference = storage.getReferenceFromUrl(
    "https://firebasestorage.googleapis.com/b/bucket/o/images%20stars.jpg",
)

Java

// Create a storage reference from our app
StorageReference storageRef = storage.getReference();

// Create a reference with an initial file path and name
StorageReference pathReference = storageRef.child("images/stars.jpg");

// Create a reference to a file from a Google Cloud Storage URI
StorageReference gsReference = storage.getReferenceFromUrl("gs://bucket/images/stars.jpg");

// Create a reference from an HTTPS URL
// Note that in the URL, characters are URL escaped!
StorageReference httpsReference = storage.getReferenceFromUrl("https://firebasestorage.googleapis.com/b/bucket/o/images%20stars.jpg");

파일 다운로드

참조를 만들었으면 getBytes() 또는 getStream()을 호출하여 Cloud Storage에서 파일을 다운로드할 수 있습니다. 다른 라이브러리로 파일을 다운로드하려는 경우 getDownloadUrl()로 다운로드 URL을 가져올 수 있습니다.

메모리에 다운로드

getBytes() 메서드로 파일을 byte[]에 다운로드합니다. 이는 파일을 다운로드하는 가장 쉬운 방법이지만 메모리에 전체 파일 콘텐츠를 로드해야 합니다. 따라서 앱의 가용 메모리보다 큰 파일을 요청하면 앱이 다운됩니다. 메모리 문제를 방지하기 위해 getBytes()는 다운로드할 최대 바이트 수를 지정합니다. 최대 크기를 앱에서 처리할 수 있는 크기로 설정하거나 다른 다운로드 방법을 사용하세요.

Kotlin+KTX

var islandRef = storageRef.child("images/island.jpg")

val ONE_MEGABYTE: Long = 1024 * 1024
islandRef.getBytes(ONE_MEGABYTE).addOnSuccessListener {
    // Data for "images/island.jpg" is returned, use this as needed
}.addOnFailureListener {
    // Handle any errors
}

Java

StorageReference islandRef = storageRef.child("images/island.jpg");

final long ONE_MEGABYTE = 1024 * 1024;
islandRef.getBytes(ONE_MEGABYTE).addOnSuccessListener(new OnSuccessListener<byte[]>() {
    @Override
    public void onSuccess(byte[] bytes) {
        // Data for "images/island.jpg" is returns, use this as needed
    }
}).addOnFailureListener(new OnFailureListener() {
    @Override
    public void onFailure(@NonNull Exception exception) {
        // Handle any errors
    }
});

로컬 파일로 다운로드

getFile() 메서드는 파일을 로컬 기기로 직접 다운로드합니다. 사용자가 오프라인 상태에서도 파일에 액세스해야 하거나 다른 앱에서 파일을 공유해야 하는 경우 이 방법을 사용하세요. getFile()은 다운로드를 관리하고 다운로드 상태를 모니터링하는 데 사용할 수 있는 DownloadTask를 반환합니다.

Kotlin+KTX

islandRef = storageRef.child("images/island.jpg")

val localFile = File.createTempFile("images", "jpg")

islandRef.getFile(localFile).addOnSuccessListener {
    // Local temp file has been created
}.addOnFailureListener {
    // Handle any errors
}

Java

islandRef = storageRef.child("images/island.jpg");

File localFile = File.createTempFile("images", "jpg");

islandRef.getFile(localFile).addOnSuccessListener(new OnSuccessListener<FileDownloadTask.TaskSnapshot>() {
    @Override
    public void onSuccess(FileDownloadTask.TaskSnapshot taskSnapshot) {
        // Local temp file has been created
    }
}).addOnFailureListener(new OnFailureListener() {
    @Override
    public void onFailure(@NonNull Exception exception) {
        // Handle any errors
    }
});

다운로드를 직접 관리하는 방법은 다운로드 관리를 참조하세요.

URL을 통해 데이터 다운로드

URL 기반 다운로드 인프라를 갖추고 있거나 단순히 공유를 위해 URL이 필요한 경우 Cloud Storage 참조에 getDownloadUrl() 메서드를 호출하여 파일의 다운로드 URL을 가져올 수 있습니다.

Kotlin+KTX

storageRef.child("users/me/profile.png").downloadUrl.addOnSuccessListener {
    // Got the download URL for 'users/me/profile.png'
}.addOnFailureListener {
    // Handle any errors
}

Java

storageRef.child("users/me/profile.png").getDownloadUrl().addOnSuccessListener(new OnSuccessListener<Uri>() {
    @Override
    public void onSuccess(Uri uri) {
        // Got the download URL for 'users/me/profile.png'
    }
}).addOnFailureListener(new OnFailureListener() {
    @Override
    public void onFailure(@NonNull Exception exception) {
        // Handle any errors
    }
});

FirebaseUI로 이미지 다운로드

FirebaseUI는 단순하고 맞춤설정 가능하고 프로덕션 환경에서 즉시 사용할 수 있는 기본 모바일 바인딩을 제공하며, 이 바인딩을 사용하면 상용구 코드를 제거하고 Google의 권장사항을 도입할 수 있습니다. Glide와 통합되는 FirebaseUI를 사용하면 Cloud Storage의 이미지를 빠르고 손쉽게 다운로드하여 캐시에 저장하고 표시할 수 있습니다.

먼저 app/build.gradle에 FirebaseUI를 추가합니다.

dependencies {
    // FirebaseUI Storage only
    implementation 'com.firebaseui:firebase-ui-storage:7.2.0'
}

이제 Cloud Storage의 이미지를 ImageView에 직접 로드할 수 있습니다.

Kotlin+KTX

// Reference to an image file in Cloud Storage
val storageReference = Firebase.storage.reference

// ImageView in your Activity
val imageView = findViewById<ImageView>(R.id.imageView)

// Download directly from StorageReference using Glide
// (See MyAppGlideModule for Loader registration)
Glide.with(context)
    .load(storageReference)
    .into(imageView)

Java

// Reference to an image file in Cloud Storage
StorageReference storageReference = FirebaseStorage.getInstance().getReference();

// ImageView in your Activity
ImageView imageView = findViewById(R.id.imageView);

// Download directly from StorageReference using Glide
// (See MyAppGlideModule for Loader registration)
Glide.with(context)
        .load(storageReference)
        .into(imageView);

활동 수명 주기 변화 처리

대화상자 표시, 화면 회전 등으로 활동 수명 주기가 변화해도 다운로드는 백그라운드에서 계속됩니다. 연결된 리스너도 연결된 채로 유지됩니다. 이 때문에 활동이 중지된 후 리스너가 호출되면 예기치 않은 결과가 나타날 수 있습니다.

활동 범위와 함께 리스너를 구독하면 활동이 중지될 때 리스너가 자동으로 등록 취소되므로 문제가 해결됩니다. 그런 다음 활동이 다시 시작될 때 getActiveDownloadTasks 메서드를 사용하여 여전히 실행 중이거나 최근에 완료된 다운로드 작업을 가져올 수 있습니다.

아래 예시에서는 이 방법과 함께 사용된 스토리지 참조 경로를 유지하는 방법을 보여 줍니다.

Kotlin+KTX

override fun onSaveInstanceState(outState: Bundle) {
    super.onSaveInstanceState(outState)

    // If there's a download 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 a download in progress, get its reference and create a new StorageReference
    val stringRef = savedInstanceState.getString("reference") ?: return

    storageRef = Firebase.storage.getReferenceFromUrl(stringRef)

    // Find all DownloadTasks under this StorageReference (in this example, there should be one)
    val tasks = storageRef.activeDownloadTasks

    if (tasks.size > 0) {
        // Get the task monitoring the download
        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 a download 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 a download 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 DownloadTasks under this StorageReference (in this example, there should be one)
    List<FileDownloadTask> tasks = mStorageRef.getActiveDownloadTasks();
    if (tasks.size() > 0) {
        // Get the task monitoring the download
        FileDownloadTask task = tasks.get(0);

        // Add new listeners to the task using an Activity scope
        task.addOnSuccessListener(this, new OnSuccessListener<FileDownloadTask.TaskSnapshot>() {
            @Override
            public void onSuccess(FileDownloadTask.TaskSnapshot state) {
                // Success!
                // ...
            }
        });
    }
}

오류 처리

다운로드 시 오류가 발생하는 이유는 파일이 없는 경우, 사용자에게 파일 액세스 권한이 없는 경우 등 다양합니다. 오류에 대한 자세한 내용은 문서의 오류 처리 섹션을 참조하세요.

전체 예시

다음은 오류 처리가 포함된 다운로드에 대한 전체 예시입니다.

Kotlin+KTX

storageRef.child("users/me/profile.png").getBytes(Long.MAX_VALUE).addOnSuccessListener {
    // Use the bytes to display the image
}.addOnFailureListener {
    // Handle any errors
}

Java

storageRef.child("users/me/profile.png").getBytes(Long.MAX_VALUE).addOnSuccessListener(new OnSuccessListener<byte[]>() {
    @Override
    public void onSuccess(byte[] bytes) {
        // Use the bytes to display the image
    }
}).addOnFailureListener(new OnFailureListener() {
    @Override
    public void onFailure(@NonNull Exception exception) {
        // Handle any errors
    }
});

Cloud Storage에 저장된 파일의 메타데이터를 가져와서 업데이트할 수도 있습니다.