Firebase Storage provides facility to upload any file like image, video, audio, etc without using server side code.
Firebase Storage is designed specifically for scale, security, and network resiliency.
Upload file to Firebase Storage is as simple as:
StorageReference storageRef = FirebaseStorage.getInstance().reference().child("folderName/file.jpg"); Uri file = Uri.fromFile(new File("path/to/folderName/file.jpg")); storageRef.putFile(file) .addOnSuccessListener(new OnSuccessListener<UploadTask.TaskSnapshot>() { @Override public void onSuccess(UploadTask.TaskSnapshot taskSnapshot) { // get a URL to the uploaded content @SuppressWarnings("VisibleForTests") Uri downloadUrl = taskSnapshot.getDownloadUrl(); } }) .addOnFailureListener(new OnFailureListener() { @Override public void onFailure(@NonNull Exception exception) { // handle unsuccessful uploads } });
First of all we have to connect our project to Firebase and then add the required dependencies. For doing this just follow below steps.
To configure the project to use the Firebase platform, open the Firebase Assistant window by clicking on Tools > Firebase. Now from the assistant go to Authentication and click Email and password aithentication. Next, press the Connect to Firebase button and make sure that the Create new Firebase project option is selected. Finaly, click Add Firebase Authentication.
Repeat the same steps for Storage.
Now you have connected Firebase platform with Authentication and Storage. Also you can manage your Firebase data from Firebase console.
Before you can begin using Firebase Storage, you'll need to either make sure your user is authenticated, or change the authentication requirement rules in the Firebase console to allow unauthenticated users to access and upload files. To keep things simple, we'll do the latter. Let's start by going into the Storage section of Firebase by selecting Storage in the left navigation column.
Next, you'll notice there are two tabs at the top of the Storage screen: Files and Rules.
Select the Rules tab, and on the line allow read, write: if request.auth != null;
, change !=
to ==
and click the PUBLISH button.
Now any user of your app should be able to upload or download files from your Firebase back-end. While this is not ideal for a production environment, it will make learning about Firebase Storage a lot easier without having to dig into authentication code.
Manually Uploading Files
While being able to upload files from an app is great, sometimes you'll simply want to store files somewhere that can be easily accessed and pulled down into your app. This is where being able to manually upload files from the Firebase Console comes into play. Under the Files tab, you'll see a blue button titled Upload File.
Click that and select the file you want to upload, and it will appear in your Firebase Storage.
Downloading file
Now that you've got a file stored in Firebase Storage, let's go ahead and pull it down into an app. We'll use a simple layout in our MainActivity
that contains an ImageView
with an id of image.
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent"> <ImageView android:id="@+id/ivImage" android:layout_centerInParent="true" android:layout_width="wrap_content" android:layout_height="wrap_content" /> </RelativeLayout>
In order to access your Firebase Storage files, you'll need to first get a reference to the FirebaseStorage
object, and then create a StorageReference
to your project's URL and the file that you want to download. You can find your project's URL at the top of the Files section of Storage in the Firebase Console.
FirebaseStorage storage = FirebaseStorage.getInstance(); StorageReference storageRef = storage.getReferenceFromUrl("gs://fileX.appspot.com").child("image.jpg");
Next, you can create a File
object and attempt to load the file you want by calling getFile
on your StorageReference
with the new File
object passed as a parameter. Since this operation happens asynchronously, you can add an OnSuccessListener
and OnFailureListener
to your call in order to handle either contingency.
try { final File localFile = File.createTempFile("image", "jpg"); storageRef.getFile(localFile).addOnSuccessListener( new OnSuccessListener<FileDownloadTask.TaskSnapshot>() { @Override public void onSuccess(FileDownloadTask.TaskSnapshot taskSnapshot) { Bitmap bitmap = BitmapFactory.decodeFile(localFile.getAbsolutePath()); ivImage.setImageBitmap(bitmap); } }).addOnFailureListener(new OnFailureListener() { @Override public void onFailure(@NonNull Exception exception) {} }); } catch (IOException e ) {}
In onSuccess()
from OnSuccessListener
, you can take the FileDownloadTask.TaskSnapshot
object and retrieve the file, which is where we will set the image to our ImageView
.
If you only need to download the file as a byte[]
and don't need it as a file, which is the more likely case when loading an image into an ImageView
, then you can retrieve the bytes in a similar fashion. But since this process will load the whole file into memory, and if we request a file larger than the available memory, it will crash. To handle this memory issue, getBytes()
takes the maximum amount of bytes to download.
final long ONE_MEGABYTE = 1024 * 1024; storageRef.getBytes(ONE_MEGABYTE).addOnSuccessListener(new OnSuccessListener<byte[]>() { @Override public void onSuccess(byte[] bytes) { Bitmap bitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.length); ivImage.setImageBitmap(bitmap); } });
There may be situations where you don't need the actual data for a stored file, but rather will want the URL. You can do this in a similar fashion as the last two examples by using the getDownloadUrl()
method on your StorageReference
, which will give you a Uri
pointing to the file's location.
storageRef.getDownloadUrl().addOnSuccessListener(new OnSuccessListener<Uri>() { @Override public void onSuccess(Uri uri) { Log.d(TAG, "uri: " + uri.toString()); } });
Uploading file
Uploading a Byte Array. As with downloading, you will need to get a reference to the FirebaseStorage
object and create a reference to your new file's storage location as a StorageReference
. For this example, we will show ic_launcher.png
in our ImageView
, and then we'll upload that as an array of bytes.
FirebaseStorage storage = FirebaseStorage.getInstance(); StorageReference storageReference = storage.getReferenceFromUrl("gs://fileX.appspot.com").child("ic_launcher.png");
Next, we will need to get a byte array from the image stored in memory via the ImageView
. This is done by retrieving it as a Bitmap
, compressing it into a ByteArrayOutputStream
, and then turning that into a byte[]
.
// get image from ImageView ivImage.setDrawingCacheEnabled(true); ivImage.measure(View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED), View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED)); ivImage.layout(0, 0, ivImage.getMeasuredWidth(), ivImage.getMeasuredHeight()); ivImage.buildDrawingCache(); Bitmap bitmap = Bitmap.createBitmap(ivImage.getDrawingCache()); ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); bitmap.compress(Bitmap.CompressFormat.JPEG, 100, outputStream); byte[] data = outputStream.toByteArray(); // get image from Drawable Bitmap icon = BitmapFactory.decodeResource(getResources(), R.drawable.image); ByteArrayOutputStream bos = new ByteArrayOutputStream(); icon.compress(Bitmap.CompressFormat.JPEG, 80, bos); byte[] data = bos.toByteArray();
Finally, you can create an UploadTask
by calling putBytes(byte[])
to upload your image to Firebase. This UploadTask
can also have an OnSuccessListener
and OnFailureListener
associated with it.
UploadTask uploadTask = storageReference.putBytes(data); uploadTask. addOnSuccessListener(new OnSuccessListener<UploadTask.TaskSnapshot>() { @Override public void onSuccess(UploadTask.TaskSnapshot taskSnapshot) { @SuppressWarnings("VisibleForTests") Uri downloadUrl = taskSnapshot.getDownloadUrl(); Log.d(TAG, "Uploaded . You can use this download url " + downloadUrl); } }) .addOnProgressListener(new OnProgressListener<UploadTask.TaskSnapshot>() { @Override public void onProgress(UploadTask.TaskSnapshot taskSnapshot) { @SuppressWarnings("VisibleForTests") long bytes = taskSnapshot.getBytesTransferred(); Log.d(TAG, "Bytes uploaded: " + bytes); //int progress = (int)(100.0 * (taskSnapshot.getBytesTransferred() / taskSnapshot.getTotalByteCount())); //progressBar.setProgress(progress); } }) .addOnFailureListener(new OnFailureListener() { @Override public void onFailure(@NonNull Exception exception) {} });
When you check the Firebase Console Storage page, you should see ic_launcher.png in your list of files.
Uploading from an InputStream or File. Now that you know how to upload a byte array, the other two types of uploads should be fairly intuitive. Let's say we have a text file named test.txt in our raw resources folder. We can read this into an InputStream
and then upload it by using the putStream(InputStream)
method of StorageReference
.
FirebaseStorage storage = FirebaseStorage.getInstance(); StorageReference storageReference = storage.getReferenceFromUrl("gs://fileX.appspot.com").child("test.txt"); // from raw resource InputStream stream = getResources().openRawResource(R.raw.test); // from drawable Bitmap image = BitmapFactory.decodeResource(getResources(), R.drawable.image); ByteArrayOutputStream bos = new ByteArrayOutputStream(); image.compress(Bitmap.CompressFormat.JPEG, 80, bos); InputStream stream = new ByteArrayInputStream(bos.toByteArray()); UploadTask uploadTask = storageReference.putStream(stream); uploadTask .addOnProgressListener(new OnProgressListener<UploadTask.TaskSnapshot>() { @Override public void onProgress(UploadTask.TaskSnapshot taskSnapshot) { @SuppressWarnings("VisibleForTests") long bytes = taskSnapshot.getBytesTransferred(); Log.d(TAG, "Bytes uploaded: " + bytes); //int progress = (int)(100.0 * (taskSnapshot.getBytesTransferred() / taskSnapshot.getTotalByteCount())); //progressBar.setProgress(progress); } }) .addOnSuccessListener(new OnSuccessListener<UploadTask.TaskSnapshot>() { @Override public void onSuccess(UploadTask.TaskSnapshot taskSnapshot) { @SuppressWarnings("VisibleForTests") Uri downloadUrl = taskSnapshot.getDownloadUrl(); Log.d(TAG, "Uploaded . You can use this download url " + downloadUrl); //progressBar.setVisibility(View.GONE); } });
Uploading an existing file is just as easy: simply get a reference to the file and call putFile(Uri
) with a URI pointing to your file. For this example, we'll just create an empty temp file in our code.
FirebaseStorage storage = FirebaseStorage.getInstance(); StorageReference storageReference = storage.getReferenceFromUrl("gs://fileX.appspot.com").child("test2.txt"); File file = null; try { file = File.createTempFile("test2", "txt"); } catch( IOException e ) {} UploadTask uploadTask = storageReference.putFile(Uri.fromFile(file));
Deleting file
As with uploading, you will need to get a reference to the FirebaseStorage
object and create a reference to your existed file's storage location as a StorageReference
.
Deleting a file:
FirebaseStorage storage = FirebaseStorage.getInstance(); StorageReference storageReference = storage.getReferenceFromUrl("gs://fileX.appspot.com").child("image.jpg"); imageRef.delete() .addOnSuccessListener(new OnSuccessListener<Void>() { @Override public void onSuccess(Void aVoid) { Log.d(TAG, "Deleted"); } }) .addOnFailureListener(new OnFailureListener() { @Override public void onFailure(@NonNull Exception exception) { Log.d(TAG, "Failed"); } });
Handling the Android Activity lifecycle
As any Android developer can attest, sometimes the Android activity lifecycle can cause unexpected issues. One of the common sources of problems is listeners that last longer than their parent Activity
, which may be the case for success/failure listeners attached to a Firebase Storage task.
If an Activity
is destroyed and recreated (such as on screen rotation) while a task is occurring, you may end up with a NullPointerException
when the task has completed. To avoid this, you will want to save your StorageReference
as a String
in your out state Bundle
in the onSaveInstanceState(Bundle)
method, and then retrieve it and add success listeners to each FileDownloadTask
or FileUploadTask
associated with that StorageReference
.
@Override protected void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); if (storageReference != null) { outState.putString(EXTRA_STORAGE_REFERENCE_KEY, storageReference.toString()); } } @Override protected void onRestoreInstanceState(Bundle savedInstanceState) { super.onRestoreInstanceState(savedInstanceState); final String stringRef = savedInstanceState.getString(EXTRA_STORAGE_REFERENCE_KEY); if (stringRef == null) { return; } storageReference = FirebaseStorage.getInstance().getReferenceFromUrl(stringRef); List<FileDownloadTask> tasks = storageReference.getActiveDownloadTasks(); for(FileDownloadTask task : tasks) { task.addOnSuccessListener(this, new OnSuccessListener<FileDownloadTask.TaskSnapshot>() { @Override public void onSuccess(FileDownloadTask.TaskSnapshot taskSnapshot) { Log.d(TAG, "download successful!"); } }); } }
How to upload file from Gallery
Following is simple example how to upload image from Gallery.
First of all, we should create a layout.
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <Button android:layout_width="match_parent" android:layout_height="wrap_content" android:text="Choose Image" android:id="@+id/btnChoose"/> <Button android:layout_width="match_parent" android:layout_height="wrap_content" android:text="Upload Image" android:id="@+id/btnUpload"/> <ImageView android:layout_width="match_parent" android:layout_height="300dp" android:id="@+id/ivImage"/> </LinearLayout>
Next, we create MainActivity.java.
public class MainActivity extends AppCompatActivity { Button btnChoose, btnUpload; ImageView ivImage; int PICK_IMAGE_REQUEST = 111; Uri filePath; ProgressDialog pd; // creating reference to firebase storage FirebaseStorage storage = FirebaseStorage.getInstance(); // change the url according to your firebase app StorageReference storageRef = storage.getReferenceFromUrl("gs://fileX.appspot.com"); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); btnChoose = (Button)findViewById(R.id.btnChoose); btnUpload = (Button)findViewById(R.id.btnUpload); ivImage = (ImageView)findViewById(R.id.ivImage); pd = new ProgressDialog(this); pd.setMessage("Uploading..."); btnChoose.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Intent intent = new Intent(); intent.setType("image/*"); intent.setAction(Intent.ACTION_PICK); startActivityForResult(Intent.createChooser(intent, "Select image"), PICK_IMAGE_REQUEST); } }); btnUpload.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { if(filePath != null) { pd.show(); StorageReference childRef = storageRef.child("image.jpg"); // uploading the image UploadTask uploadTask = childRef.putFile(filePath); uploadTask.addOnSuccessListener(new OnSuccessListener<UploadTask.TaskSnapshot>() { @Override public void onSuccess(UploadTask.TaskSnapshot taskSnapshot) { pd.dismiss(); Toast.makeText(MainActivity.this, "Upload successful", Toast.LENGTH_SHORT).show(); } }).addOnFailureListener(new OnFailureListener() { @Override public void onFailure(@NonNull Exception e) { pd.dismiss(); Toast.makeText(MainActivity.this, "Upload Failed -> " + e, Toast.LENGTH_SHORT).show(); } }); } else { Toast.makeText(MainActivity.this, "Select an image", Toast.LENGTH_SHORT).show(); } } }); } @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); if (requestCode == PICK_IMAGE_REQUEST && resultCode == RESULT_OK && data != null && data.getData() != null) { filePath = data.getData(); try { //getting image from gallery Bitmap bitmap = MediaStore.Images.Media.getBitmap(getContentResolver(), filePath); //Setting image to ImageView ivImage.setImageBitmap(bitmap); } catch (Exception e) { e.printStackTrace(); } } } }
Result