Android で Cloud Storage を使用してファイルをアップロードする

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

ダウンロード URL を取得する

ファイルをアップロードした後、StorageReferencegetDownloadUrl() メソッドを呼び出して、ファイルをダウンロードするための 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
            // ...
        }
    }
});

ファイル メタデータを追加する

ファイルをアップロードするときにメタデータを含めることもできます。このメタデータには、namesizecontentType(一般的に MIME タイプと呼ばれます)などの標準的なファイル メタデータのプロパティが含まれます。putFile() メソッドでは File 拡張子から MIME タイプが自動的に推測されますが、contentType をメタデータに指定することにより、自動検出されるタイプをオーバーライドできます。contentType が指定されず、Cloud Storage がファイル拡張子からデフォルトのコンテンツ タイプを自動的に推測することもできない場合は、Cloud Storageapplication/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 アップロードが失敗すると呼び出されます。アップロードの失敗は、ネットワークのタイムアウトや認証の失敗が原因で発生します。またはタスクをキャンセルした場合にも発生します。

OnFailureListenerException インスタンスとともに呼び出されます。その他のリスナーは、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");
    }
});

アクティビティのライフサイクルの変更を処理する

アクティビティのライフサイクル(ダイアログの表示や画面の回転など)が変わった後でも、アップロードはバックグラウンドで継続します。アタッチしたリスナーも、アタッチされたまま維持されます。このため、アクティビティが停止した後でリスナーが呼び出されると、予期せぬ結果が生じることがあります。

この問題を解決するには、アクティビティの範囲を指定してリスナーを登録します。こうすると、そのアクティビティが停止したときにリスナーは自動的に登録を解除されます。次に、アクティビティが再開したら 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);

セッションは 1 週間維持されます。セッションの期限が切れた後でセッションを再開しようとした場合、またはエラーが発生した場合は、失敗のコールバックを受け取ります。アップロードの間にファイルが変更されていないことを必ずご確認ください。

エラー処理

アップロード時にエラーが発生する理由として、ローカル ファイルが存在しない、目的のファイルをアップロードする権限がユーザーにないなど、たくさんの理由が考えられます。エラーについて詳しくは、このドキュメントのエラーの処理のセクションをご覧ください。

アップロード、進捗状況のモニタリング、エラー処理の完全な例を以下に示します。

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 からファイルをダウンロードする方法を学習しましょう。