1.複数のファイルをアップロードできない!
Android
でFirebaseStorage
を使っているとき当然複数のファイルを一回にアップロードすることは当然あります。しかし、現在そのようなAPIは提供されていません。困った困った...。ということで複数のファイルをZipにまとめてアップロードすることにしました。
2.実装
SimpleFirebaseStorageDatabase.java
import android.content.Context;
import android.net.Uri;
import android.util.Log;
import androidx.annotation.NonNull;
import com.google.android.gms.tasks.OnFailureListener;
import com.google.android.gms.tasks.OnSuccessListener;
import com.google.firebase.auth.FirebaseAuth;
import com.google.firebase.auth.FirebaseUser;
import com.google.firebase.database.DatabaseReference;
import com.google.firebase.storage.FirebaseStorage;
import com.google.firebase.storage.StorageReference;
import com.google.firebase.storage.UploadTask;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
public class SimpleFirebaseStorageDatabase {
private static final String TAG = "SimpleFirebaseStorageDB";
private String userId;
private StorageReference storageRef;
private DatabaseReference databaseRef;
private Context mContext;
private static final long MAX_DOWNLOAD_SIZE = 1024 * 1024 * 5;
public SimpleFirebaseStorageDatabase (Context context, FirebaseAuth mAuth, String firebaseStorageBucketToken) {
mContext = context;
FirebaseUser user = mAuth.getCurrentUser();
if (user == null) {
return;
}
this.userId = user.getUid();
storageRef = FirebaseStorage.getInstance().getReferenceFromUrl(firebaseStorageBucketToken).child("users").child(userId);
}
public void uploadFilesByTempFile (final String projectName, List<String> uris) {
File zip = zipFilesToFile(projectName, uris);
if (zip == null) {
Log.e(TAG, "No zip file is created: " + projectName);
return;
}
try {
storageRef.child(projectName).putFile(Uri.fromFile(zip)).addOnSuccessListener(new OnSuccessListener<UploadTask.TaskSnapshot>() {
@Override
public void onSuccess (UploadTask.TaskSnapshot taskSnapshot) {
Log.d(TAG, "Succeeded to upload project : " + projectName);
storageRef.child(projectName).getDownloadUrl().addOnSuccessListener(new OnSuccessListener<Uri>() {
@Override
public void onSuccess (Uri uri) {
Log.d(TAG, "Succeeded to get download url : " + uri);
onUploadSuccess(uri.toString());
}
}).addOnFailureListener(new OnFailureListener() {
@Override
public void onFailure (@NonNull Exception e) {
Log.e(TAG, "Failed to get download url : " + projectName, e);
onUploadFail();
}
});
}
}).addOnFailureListener(new OnFailureListener() {
@Override
public void onFailure (@NonNull Exception e) {
Log.e(TAG, "Failed to upload project : " + projectName, e);
onUploadFail();
}
});
} catch (Exception e) {
onUploadFail();
}
}
private File zipFilesToFile(String projectName, List<String> uris) {
try {
File projFile = new File(mContext.getFilesDir(), projectName);
Log.d(TAG, "Make project zip file: " + projFile.getPath());
// String outfilePath で出力先のファイルパスが指定されているとする
ZipOutputStream out = new ZipOutputStream(new FileOutputStream(projFile));
byte[] buffer = new byte[1024 * 4];
int len = 0;
// 引数などで String[] files として zip に追加したいファイルのファイルパスが指定されているとする
for (String uri : uris) {
zipOneFile(uri, out, buffer);
}
out.close();
Log.d(TAG, "Succeeded to make project zip file: " + projFile.getPath());
return projFile;
} catch(Exception e){
// エラー処理
Log.e(TAG, "Failed to make project zip file: " + projectName, e);
}
return null;
}
/**
* 指定Uriの1ファイルをZIPに追加する。
* @param uri 圧縮するファイルのパス(Uri)
* @param os
* @param buffer
*/
private static void zipOneFile(String uri, ZipOutputStream os, byte[] buffer) {
InputStream in = null;
int len = 0;
try {
Log.d(TAG, "Zip one file : " + uri);
String path = Uri.parse(uri).getPath();
File inFile = new File(path);
if (!inFile.exists()) {
Log.w(TAG, "File doesn't exist: " + uri);
return;
}
in = new FileInputStream(inFile);
os.putNextEntry(new ZipEntry(inFile.getName())); // path名からファイル名だけにしておく
while ((len = in.read(buffer)) > 0) {
os.write(buffer, 0, len);
}
} catch (Exception e) {
Log.e(TAG, "Failed to zip file: " + uri, e);
} finally {
if (in != null) {
try {
in.close();
} catch (IOException ioe) {
// do nothing
}
}
}
}
private void onUploadSuccess (String downloadUri) {
if (mContext instanceof OnFirebaseStorageUploadListener) {
((OnFirebaseStorageUploadListener) mContext).onUploadSucceed(downloadUri);
}
}
private void onUploadFail () {
if (mContext instanceof OnFirebaseStorageUploadListener) {
((OnFirebaseStorageUploadListener) mContext).onUploadFailed();
}
}
public interface OnFirebaseStorageUploadListener {
void onUploadSucceed (String downloadUri);
void onUploadFailed ();
}
3.使用方法
MainActivity.java
public class MainActivity extends AppCompatActivity implements SimpleFirebaseStorageDatabase.OnFirebaseStorageUploadListener{
private List<String> mUriList;
@Override
protected void onCreate (Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
SimpleFirebaseStorageDatabase firebaseStorage = new SimpleFirebaseStorageDatabase(ShareProjectActivity.this, mAuth, "バッケトトークン");
mUriList = // ...
firebaseStorage.uploadFilesByTempFile("ファイル名.zip", mUriList);
}
@Override
public void onUploadSucceed (String downloadUri) {
// 成功
}
@Override
public void onUploadFailed () {
// 失敗
}
まとめ
今回は解説なしです。これに対応するダウンロードの処理は後日また書きます。
Firebaseを利用する際に必要になるログイン処理に関してはこちら
Twitter(フォローよろしくお願いします。): https://twitter.com/Cyber_Hacnosuke