Firebase Storage (Java)
Firebase Cloud Storage is where user-generated files live — for RecipeBox, that means the actual recipe photo files, as opposed to the recipe's text data. It shares the same Firebase project and the same signed-in-user model as Firebase Auth and Firestore, so access can be controlled with the same per-user rules approach already used for both.
StorageReference: A Pointer Into the Bucket
A StorageReference doesn't hold a file itself — it points at a location inside your project's storage bucket, the same way a File path points at a location on disk without necessarily containing anything yet.
FirebaseStorage storage = FirebaseStorage.getInstance();
StorageReference storageRef = storage.getReference();
// A pointer to where this specific recipe's photo will live
StorageReference photoRef = storageRef.child("recipe_photos/" + recipeId + ".jpg");
Uploading a File
putFile() uploads a local file (typically a Uri from the camera or gallery, the same photoUri concept used on the Recipe class) and returns an UploadTask, which supports progress, success, and failure listeners.
Uri localFileUri = Uri.fromFile(new File(localPhotoPath)); // the photo just taken/picked
UploadTask uploadTask = photoRef.putFile(localFileUri);
uploadTask.addOnProgressListener(taskSnapshot -> {
double percent = (100.0 * taskSnapshot.getBytesTransferred()) / taskSnapshot.getTotalByteCount();
Log.d("RecipeBox", "Upload progress: " + percent + "%");
}).addOnFailureListener(e -> {
Log.e("RecipeBox", "Photo upload failed", e);
}).addOnSuccessListener(taskSnapshot -> {
Log.d("RecipeBox", "Photo uploaded successfully");
// fetch the download URL next — see below
});
- addOnProgressListener() is useful for showing an upload progress bar for larger photo files.
- Uploads happen off the main thread automatically — no manual ExecutorService needed here, unlike the Room writes covered earlier.
Getting the Download URL
A successful upload doesn't hand back a usable URL by itself — that requires one more call, getDownloadUrl(), on the completed task's storage reference.
uploadTask.addOnSuccessListener(taskSnapshot -> {
taskSnapshot.getStorage().getDownloadUrl().addOnSuccessListener(downloadUri -> {
String photoUrl = downloadUri.toString();
// Save the URL string on the Recipe document in Firestore (previous lesson)
firestoreDb.collection("recipes").document(recipeId)
.update("photoUri", photoUrl);
});
});
Displaying the Uploaded Photo Later
Once a recipe's photoUri field holds a Storage download URL, showing it is no different from loading any other image URL — an image-loading library (such as Glide or Picasso, covered conceptually alongside ImageView & ImageButton) can load it straight into an ImageView without any Storage-specific code.
Glide.with(context)
.load(recipe.photoUri) // the download URL saved earlier
.into(recipeImageView);
Storage Security Rules
Just as Firestore Security Rules are the real enforcement behind "a user can only access their own recipes," Storage Security Rules enforce the same principle for files, evaluated on Google's servers for every upload and download request.
rules_version = '2';
service firebase.storage {
match /b/{bucket}/o {
match /recipe_photos/{allPaths=**} {
allow read, write: if request.auth != null;
}
}
}