Android에서 파일 업로드

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

파일 업로드

Cloud Storage에 파일을 업로드하려면 우선 파일 이름을 포함하여 파일의 전체 경로를 가리키는 참조를 만듭니다.

자바
Android

// 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

Kotlin
Android

// 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

적절한 참조를 만들었으면 putBytes(), putFile() 또는 putStream() 메소드를 호출하여 Cloud Storage에 파일을 업로드합니다.

Google Cloud Storage 버킷의 루트를 가리키는 참조로는 데이터를 업로드할 수 없습니다. 참조는 하위 URL을 가리켜야 합니다.

메모리 데이터에서 업로드

putBytes() 메소드는 Cloud Storage에 파일을 업로드하는 가장 간단한 방법입니다. putBytes()byte[]를 취하고 UploadTask를 반환하며 이 반환 개체를 사용하여 업로드를 관리하고 상태를 모니터링할 수 있습니다.

자바
Android

// 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.
        // ...
    }
});

Kotlin
Android

// 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.metadata contains file metadata such as size, content-type, etc.
    // ...
}

putBytes()byte[]를 취하므로 앱은 파일의 전체 내용을 메모리에 한번에 담아야 합니다. 메모리를 절약하려면 putStream() 또는 putFile()을 사용하세요.

스트림에서 업로드

putStream() 메소드는 Cloud Storage에 파일을 업로드하는 가장 범용적인 방법입니다. putStream()InputStream을 취하고 UploadTask를 반환하며 이 반환 개체를 사용하여 업로드를 관리하고 상태를 모니터링할 수 있습니다.

자바
Android

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.
        // ...
    }
});

Kotlin
Android

val stream = FileInputStream(File("path/to/images/rivers.jpg"))

uploadTask = mountainsRef.putStream(stream)
uploadTask.addOnFailureListener {
    // Handle unsuccessful uploads
}.addOnSuccessListener {
    // taskSnapshot.metadata contains file metadata such as size, content-type, etc.
    // ...
}

로컬 파일에서 업로드

putFile() 메소드를 사용하여 카메라의 사진, 동영상과 같이 기기의 로컬 파일을 업로드할 수 있습니다. putFile()File을 취하고 UploadTask를 반환하며 이 반환 개체를 사용하여 업로드를 관리하고 상태를 모니터링할 수 있습니다.

자바
Android

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.
        // ...
    }
});

Kotlin
Android

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.metadata contains file metadata such as size, content-type, etc.
    // ...
}

다운로드 URL 가져오기

파일을 업로드한 후 StorageReference에서 getDownloadUrl() 메소드를 호출하면 파일 다운로드 URL을 가져올 수 있습니다.

자바
Android

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
            // ...
        }
    }
});

Kotlin
Android

val ref = storageRef.child("images/mountains.jpg")
uploadTask = ref.putFile(file)

val urlTask = uploadTask.continueWithTask(Continuation<UploadTask.TaskSnapshot, Task<Uri>> { task ->
    if (!task.isSuccessful) {
        task.exception?.let {
            throw it
        }
    }
    return@Continuation ref.downloadUrl
}).addOnCompleteListener { task ->
    if (task.isSuccessful) {
        val downloadUri = task.result
    } else {
        // Handle failures
        // ...
    }
}

파일 메타데이터 추가

파일을 업로드할 때 메타데이터를 포함할 수도 있습니다. 이 메타데이터는 name, size, contentType(통칭 MIME 형식) 등의 일반적인 파일 메타데이터 속성을 포함합니다. putFile() 메소드는 File 확장자에서 MIME 형식을 자동으로 유추하지만, 메타데이터에 contentType을 지정하면 자동으로 감지된 유형을 재정의할 수 있습니다. contentType을 제공하지 않았고 Cloud Storage가 파일 확장자에서 기본값을 유추할 수 없는 경우에는 application/octet-stream이 사용됩니다. 파일 메타데이터의 자세한 내용은 파일 메타데이터 사용 섹션을 참조하세요.

자바
Android

// 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);

Kotlin
Android

// Create file metadata including the content type
var metadata = StorageMetadata.Builder()
        .setContentType("image/jpg")
        .build()

// Upload the file and metadata
uploadTask = storageRef.child("images/mountains.jpg").putFile(file, metadata)

업로드 관리

업로드 시작과 더불어 pause(), resume(), cancel() 메소드를 사용하여 업로드를 일시중지, 재개, 취소할 수 있습니다. 일시중지 및 재개 이벤트는 각각 pauseprogress 상태 변경을 발생시킵니다. 업로드를 취소하면 업로드 취소를 알리는 오류와 함께 업로드가 실패하게 됩니다.

자바
Android

uploadTask = storageRef.child("images/mountains.jpg").putFile(file);

// Pause the upload
uploadTask.pause();

// Resume the upload
uploadTask.resume();

// Cancel the upload
uploadTask.cancel();

Kotlin
Android

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 업로드가 실패했을 때 호출됩니다. 네트워크 시간 초과, 승인 실패, 작업 취소 등의 이유로 실패할 수 있습니다.

OnFailureListenerException 인스턴스와 함께 호출됩니다. 다른 리스너는 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 이벤트 리스너로 업로드 이벤트를 간편하고 유용하게 모니터링할 수 있습니다.

자바
Android

// 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();
        System.out.println("Upload is " + progress + "% done");
    }
}).addOnPausedListener(new OnPausedListener<UploadTask.TaskSnapshot>() {
    @Override
    public void onPaused(UploadTask.TaskSnapshot taskSnapshot) {
        System.out.println("Upload is paused");
    }
});

Kotlin
Android

// Observe state change events such as progress, pause, and resume
uploadTask.addOnProgressListener { taskSnapshot ->
    val progress = (100.0 * taskSnapshot.bytesTransferred) / taskSnapshot.totalByteCount
    System.out.println("Upload is $progress% done")
}.addOnPausedListener {
    System.out.println("Upload is paused")
}

활동 주기 변화 처리

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

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

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

자바
Android

@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!
                // ...
            }
        });
    }
}

Kotlin
Android

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

    // If there's an upload in progress, save the reference so you can query it later
    storageRef?.let {
        outState.putString("reference", it.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 = FirebaseStorage.getInstance().getReferenceFromUrl(stringRef)

    // Find all UploadTasks under this StorageReference (in this example, there should be one)

    storageRef?.activeUploadTasks?.let { it ->
        if (it.size > 0) {
            // Get the task monitoring the upload
            val task = it[0]

            // Add new listeners to the task using an Activity scope
            task.addOnSuccessListener(this) {
                // Success!
                // ...
            }
        }
    }
}

getActiveUploadTasks는 제공된 참조 위치와 그 아래 위치에 있는 모든 활성 업로드 작업을 검색하므로 여러 작업을 처리해야 할 수 있습니다.

프로세스 재시작 시 업로드 계속

프로세스가 종료되면 진행 중인 업로드가 모두 중단됩니다. 그러나 프로세스가 다시 시작되면 서버와 업로드 세션을 재개하여 업로드를 계속할 수 있습니다. 따라서 업로드가 파일 처음부터 다시 시작되지 않으므로 시간과 대역폭이 절약됩니다.

이렇게 하려면 putFile을 통해 업로드를 시작합니다. 결과로 반환된 StorageTask에 대해 getUploadSessionUri를 호출하고 결과 값을 SharedPreferences 등의 영구 저장소에 저장합니다.

자바
Android

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.
        }
    }
});

Kotlin
Android

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.
    }
}

업로드가 중단되었던 프로세스가 다시 시작되면 putFile을 다시 호출합니다. 이번에는 저장해 둔 URI를 함께 전달합니다.

자바
Android

//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
Android

// 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.Builder().build(), sessionUri)

세션은 1주일 동안 지속됩니다. 만료되었거나 오류가 발생한 세션을 재개하려고 하면 실패 콜백이 수신됩니다. 개발자는 업로드 중간에 파일이 변경되지 않도록 조치해야 합니다.

오류 처리

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

전체 예

다음은 업로드, 진행률 모니터링, 오류 처리를 보여주는 예시입니다.

자바
Android

// 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();
        System.out.println("Upload is " + progress + "% done");
    }
}).addOnPausedListener(new OnPausedListener<UploadTask.TaskSnapshot>() {
    @Override
    public void onPaused(UploadTask.TaskSnapshot taskSnapshot) {
        System.out.println("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
        // ...
    }
});

Kotlin
Android

// File or Blob
file = Uri.fromFile(File("path/to/mountains.jpg"))

// Create the file metadata
metadata = StorageMetadata.Builder()
        .setContentType("image/jpeg")
        .build()

// 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.
uploadTask.addOnProgressListener { taskSnapshot ->
    val progress = (100.0 * taskSnapshot.bytesTransferred) / taskSnapshot.totalByteCount
    System.out.println("Upload is $progress% done")
}.addOnPausedListener {
    System.out.println("Upload is paused")
}.addOnFailureListener {
    // Handle unsuccessful uploads
}.addOnSuccessListener {
    // Handle successful uploads on complete
    // ...
}

이제 파일 업로드가 끝났으므로 Cloud Storage에서 다운로드하는 방법을 알아보세요.

다음에 대한 의견 보내기...

도움이 필요하시나요? 지원 페이지를 방문하세요.