
Firebase Storage per Android (Java) — Guida completa
In questa guida completa vedrai come integrare Firebase Storage in un’app Android scritta in Java: dalla configurazione del progetto fino al caricamento, download, eliminazione dei file, gestione degli URL di download, monitoraggio del progresso e gestione robusta degli errori. Inoltre, tutti gli esempi sono aggiornati per l’uso con Android Studio e Gradle. In particolare, questa guida su Firebase Storage Android Java ti fornirà tutto il necessario per gestire file nel cloud nella tua app.
Cos’è Firebase Storage per Android Java
Firebase Storage è un servizio di Google pensato per salvare e servire file (immagini, video, PDF, documenti, audio, ecc.) in modo scalabile e sicuro. In particolare, si basa su Google Cloud Storage, ma espone un SDK semplificato per Android, iOS, Web e backend.
Caratteristiche principali:
- Scalabile: gestisce automaticamente grandi quantità di file e traffico.
- Sicuro: usa le regole di sicurezza di Firebase con integrazione nativa di Firebase Authentication.
- Affidabile: supporta ripresa automatica degli upload e dei download interrotti.
- Semplice: API ad alto livello per le operazioni più comuni (upload, download, cancellazione, URL pubblici).
Prerequisiti
- Android Studio installato.
- Conoscenza base di Java e Android (Activity, Intent, permessi runtime).
- Un progetto Firebase creato dalla console (https://console.firebase.google.com).
Configurazione del progetto Android con Firebase Storage
1. Creazione progetto Firebase e aggiunta app Android
1. Vai su Firebase Console e crea un nuovo progetto (o usane uno esistente).
2. Aggiungi una nuova app Android inserendo il package name esatto del tuo progetto (ad es. com.example.myapp).
3. Scarica il file google-services.json generato da Firebase.
Copia il file google-services.json nella cartella app/ del tuo progetto Android.
2. Configurazione Gradle (Project)
Apri il file build.gradle (Project) e assicurati di avere il plugin Google Services nel blocco dependencies di buildscript o nel settings.gradle a seconda della versione di Gradle. Un esempio classico è il seguente:
buildscript {
dependencies {
classpath 'com.google.gms:google-services:4.4.2' // versione recente
}
}
3. Dipendenze Gradle (Module: app)
Nel file build.gradle (Module: app) aggiungi le dipendenze per Firebase Storage (e, se ti serve, per Authentication per gestire utenti loggati):
plugins {
id 'com.android.application'
id 'com.google.gms.google-services'
}
android {
namespace 'com.example.myapp'
compileSdk 34
defaultConfig {
applicationId "com.example.myapp"
minSdk 21
targetSdk 34
versionCode 1
versionName "1.0"
}
}
dependencies {
implementation platform('com.google.firebase:firebase-bom:33.2.0')
// Firebase Storage
implementation 'com.google.firebase:firebase-storage'
// (Opzionale) Firebase Authentication
implementation 'com.google.firebase:firebase-auth'
// Altre dipendenze app...
}
Sincronizza il progetto con Gradle (Sync Now).
4. Inizializzazione Firebase
Firebase viene inizializzato automaticamente se il tuo progetto è correttamente configurato. Se per qualche motivo usi un Application custom, puoi inizializzarlo manualmente:
public class MyApp extends Application {
@Override
public void onCreate() {
super.onCreate();
FirebaseApp.initializeApp(this);
}
}
Ricorda di dichiarare l’Application nel AndroidManifest.xml se la usi.
Struttura base di Firebase Storage in Android Java
L’oggetto principale è FirebaseStorage, da cui ottieni riferimenti (StorageReference) alle cartelle e ai file.
// Ottieni l'istanza principale di Firebase Storage
FirebaseStorage storage = FirebaseStorage.getInstance();
// Riferimento root (simile alla cartella principale)
StorageReference storageRef = storage.getReference();
// Esempio: riferimento a una "cartella" immagini profilo
StorageReference profileImagesRef = storageRef.child("profile_images");
// Esempio: riferimento a un singolo file
StorageReference singleImageRef = profileImagesRef.child("user123.jpg");
I percorsi sono stringhe tipo filesystem (cartella/sotto-cartella/file.ext), anche se dietro le quinte sono bucket di Google Cloud Storage.
Gestione permessi runtime (lettura da galleria / scrittura su storage)
Per leggere immagini o documenti dalla memoria del dispositivo (galleria, file manager) serve gestire i permessi runtime (es. READ_EXTERNAL_STORAGE su versioni più vecchie di Android) oppure usare il nuovo sistema di Storage Access Framework. Pertanto, per una guida dettagliata, vedi la guida correlata sui permessi in fondo alla pagina.
Upload di file su Firebase Storage Android Java
Ci sono due casi tipici: anzitutto, l’upload da URI locale (file scelto dalla galleria o da un file picker), e in secondo luogo, l’upload da byte[] o InputStream (ad es. un’immagine compressa o generata in memoria).
- Upload da URI locale (file scelto dalla galleria o da un file picker).
- Upload da byte[] o InputStream (ad es. un’immagine compressa o generata in memoria).
Esempio: scegliere un’immagine dalla galleria e caricarla
Supponiamo di avere un pulsante che apre la galleria. Successivamente, carichiamo l’immagine selezionata in Firebase Storage.
public class UploadImageActivity extends AppCompatActivity {
private static final int REQUEST_CODE_IMAGE_PICKER = 1001;
private FirebaseStorage storage;
private StorageReference storageRef;
private ProgressBar progressBar;
private TextView textStatus;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_upload_image);
storage = FirebaseStorage.getInstance();
storageRef = storage.getReference();
progressBar = findViewById(R.id.progressBar);
textStatus = findViewById(R.id.textStatus);
Button buttonChoose = findViewById(R.id.buttonChooseImage);
Button buttonUpload = findViewById(R.id.buttonUploadImage);
buttonChoose.setOnClickListener(v -> openImagePicker());
buttonUpload.setOnClickListener(v -> {
if (selectedImageUri != null) {
uploadImageToFirebase(selectedImageUri);
}
});
}
private Uri selectedImageUri;
private void openImagePicker() {
Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
intent.setType("image/*");
startActivityForResult(Intent.createChooser(intent, "Seleziona un'immagine"),
REQUEST_CODE_IMAGE_PICKER);
}
@Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == REQUEST_CODE_IMAGE_PICKER && resultCode == RESULT_OK && data != null) {
selectedImageUri = data.getData();
textStatus.setText("Immagine selezionata: " + selectedImageUri.toString());
}
}
private void uploadImageToFirebase(Uri fileUri) {
progressBar.setProgress(0);
progressBar.setVisibility(View.VISIBLE);
textStatus.setText("Caricamento in corso...");
// Creiamo un nome file univoco basato sul timestamp
String fileName = "images/" + System.currentTimeMillis() + ".jpg";
StorageReference imageRef = storageRef.child(fileName);
UploadTask uploadTask = imageRef.putFile(fileUri);
// Listener per progresso
uploadTask.addOnProgressListener(taskSnapshot -> {
double progress = (100.0 * taskSnapshot.getBytesTransferred()) /
taskSnapshot.getTotalByteCount();
progressBar.setProgress((int) progress);
textStatus.setText("Caricamento: " + (int) progress + "%");
});
// Listener success
uploadTask.addOnSuccessListener(taskSnapshot -> {
progressBar.setVisibility(View.GONE);
textStatus.setText("Upload completato");
// Recupera URL di download
imageRef.getDownloadUrl()
.addOnSuccessListener(uri -> {
String downloadUrl = uri.toString();
textStatus.setText("Upload completato. URL: " + downloadUrl);
})
.addOnFailureListener(e -> {
textStatus.setText("Upload ok, ma errore nel recupero URL: " + e.getMessage());
});
});
// Listener errori
uploadTask.addOnFailureListener(e -> {
progressBar.setVisibility(View.GONE);
textStatus.setText("Errore upload: " + e.getMessage());
});
}
}
In questo esempio usiamo putFile(Uri) poiché questo è il metodo più comune quando ricevi un file da un Intent.
Upload di documenti (PDF, DOCX, ecc.)
Per documenti è praticamente identico al metodo precedente: tuttavia, cambia solo il tipo MIME al momento della selezione.
private void openDocumentPicker() {
Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
intent.setType("application/pdf");
// oppure "application/*" per qualsiasi tipo di documento
startActivityForResult(Intent.createChooser(intent, "Seleziona un documento"),
REQUEST_CODE_DOCUMENT_PICKER);
}
private void uploadDocumentToFirebase(Uri fileUri) {
String fileName = "docs/" + System.currentTimeMillis() + ".pdf";
StorageReference docRef = storageRef.child(fileName);
UploadTask uploadTask = docRef.putFile(fileUri);
// Aggiungi listener come nell'esempio immagine
}
Upload da byte[] (ad es. immagine compressa)
A volte vuoi comprimere l’immagine prima di caricarla (es. JPEG con compressione). A tal fine, puoi usare putBytes(byte[]):
private void uploadCompressedBitmap(Bitmap bitmap) {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
bitmap.compress(Bitmap.CompressFormat.JPEG, 80, baos); // 80% qualità
byte[] data = baos.toByteArray();
StorageReference imageRef = storageRef.child("compressed/" + System.currentTimeMillis() + ".jpg");
UploadTask uploadTask = imageRef.putBytes(data);
uploadTask.addOnSuccessListener(taskSnapshot -> {
// Successo
}).addOnFailureListener(e -> {
// Errore
});
}
Download di file da Firebase Storage su Android Java
Per scaricare un file hai diverse opzioni disponibili. In primo luogo, puoi scaricare in memoria come byte[]; in secondo luogo, scaricare in un file locale (es. in cache); infine, sfruttare direttamente l’URL di download con Glide/Picasso (per immagini) o un browser (per PDF, ecc.).
- Scaricare in memoria come
byte[]. - Scaricare in un file locale (es. in cache).
- Sfruttare direttamente l’URL di download con Glide/Picasso (per immagini) o un browser (per PDF, ecc.).
Scaricare un file in memoria (byte[])
private void downloadFileToMemory() {
StorageReference fileRef = storageRef.child("docs/manuale.pdf");
final long ONE_MEGABYTE = 1024 * 1024;
fileRef.getBytes(ONE_MEGABYTE)
.addOnSuccessListener(bytes -> {
// bytes contiene il contenuto del file (fino a 1 MB in questo esempio)
})
.addOnFailureListener(e -> {
// Gestisci errore
});
}
Scaricare in un file locale
private void downloadFileToLocalFile() throws IOException {
StorageReference fileRef = storageRef.child("docs/manuale.pdf");
File localFile = File.createTempFile("manuale", ".pdf");
fileRef.getFile(localFile)
.addOnSuccessListener(taskSnapshot -> {
// Il file è stato salvato in localFile
})
.addOnFailureListener(e -> {
// Gestisci errore
});
}
Caricare un’immagine in un ImageView con Glide usando l’URL
Dopo aver ottenuto l’URL di download, puoi usare librerie come Glide per caricare le immagini direttamente senza gestire manualmente il download:
private void loadImageIntoImageView(StorageReference imageRef, ImageView imageView) {
imageRef.getDownloadUrl()
.addOnSuccessListener(uri -> {
Glide.with(this)
.load(uri)
.into(imageView);
})
.addOnFailureListener(e -> {
// Errore nel recupero URL
});
}
Gestione degli URL di download
Ogni file caricato su Firebase Storage può avere un download URL firmato. Pertanto, puoi usarlo per mostrare immagini, condividere file, oppure salvarlo in Firestore/Realtime Database per un accesso successivo. Una volta completato il processo di upload su Firebase Storage Android Java, avrai sempre la possibilità di recuperare questi URL e integrarli nell’architettura della tua applicazione mobile.
imageRef.getDownloadUrl()
.addOnSuccessListener(uri -> {
String downloadUrl = uri.toString();
// Salva l'URL nel tuo database o usalo direttamente
})
.addOnFailureListener(e -> {
// Gestione errore
});
Buona pratica: in primo luogo, non salvare nel database percorsi locali o riferimenti di tipo StorageReference serializzati; invece, usa l’URL di download oppure la stringa del percorso nel bucket (es. images/user123/profile.jpg).
Eliminazione di file da Firebase Storage
Per eliminare un file devi avere il suo StorageReference (ottenuto dal percorso oppure da un URL di download convertito in riferimento).
private void deleteFileFromStorage() {
StorageReference fileRef = storageRef.child("images/user123/profile.jpg");
fileRef.delete()
.addOnSuccessListener(unused -> {
// File eliminato correttamente
})
.addOnFailureListener(e -> {
// Errore, ad esempio file inesistente o permessi insufficienti
});
}
Inoltre, puoi anche ottenere un StorageReference da un URL di download con FirebaseStorage.getReferenceFromUrl(“…”).
String url = "https://firebasestorage.googleapis.com/v0/b/tuo-bucket/o/images%2Fuser123%2Fprofile.jpg?...";
StorageReference fileRefFromUrl = storage.getReferenceFromUrl(url);
fileRefFromUrl.delete();
Listener di progresso per upload e download
Monitorare il progresso è importante per migliorare l’esperienza utente. Per esempio, puoi mostrare una progress bar durante le operazioni di upload e download.
Progresso upload
UploadTask uploadTask = imageRef.putFile(fileUri);
uploadTask.addOnProgressListener(taskSnapshot -> {
double progress = (100.0 * taskSnapshot.getBytesTransferred())
/ taskSnapshot.getTotalByteCount();
Log.d("UPLOAD", "Progresso: " + progress + "%");
});
Progresso download
File localFile = File.createTempFile("immagine", ".jpg");
fileRef.getFile(localFile)
.addOnProgressListener(taskSnapshot -> {
double progress = (100.0 * taskSnapshot.getBytesTransferred())
/ taskSnapshot.getTotalByteCount();
Log.d("DOWNLOAD", "Progresso: " + progress + "%");
})
.addOnSuccessListener(taskSnapshot -> {
// Download completato
})
.addOnFailureListener(e -> {
// Errore
});
Gestione degli errori in Firebase Storage
Gli errori più comuni riguardano diversi aspetti critici. In primo luogo, pochi permessi nelle regole di sicurezza; in secondo luogo, file inesistente o percorso errato; quindi, problemi di rete; infine, quota superata o limiti di dimensione file.
- Pochi permessi nelle regole di sicurezza.
- File inesistente o percorso errato.
- Problemi di rete.
- Quota superata o limiti di dimensione file.
Gli errori vengono restituiti come Exception (tipicamente StorageException). Pertanto, puoi controllare il codice di errore:
uploadTask.addOnFailureListener(e -> {
if (e instanceof StorageException) {
StorageException se = (StorageException) e;
int errorCode = se.getErrorCode();
switch (errorCode) {
case StorageException.ERROR_OBJECT_NOT_FOUND:
// File inesistente
break;
case StorageException.ERROR_NOT_AUTHENTICATED:
// Utente non autenticato
break;
case StorageException.ERROR_NOT_AUTHORIZED:
// Regole non permettono l'accesso
break;
case StorageException.ERROR_RETRY_LIMIT_EXCEEDED:
// Troppi tentativi (problemi di rete persistenti)
break;
default:
// Altro errore
break;
}
}
});
Combina questa gestione con messaggi chiari per l’utente (Toast, Snackbar, dialog) e, inoltre, se serve, log dettagliati per il debugging. Per massimizzare la sicurezza e l’efficienza della tua app, considera questi suggerimenti di best practice che applicherai quando implementerai Firebase Storage Android Java nel tuo progetto.
Esempio pratico completo: upload immagine con Firebase Storage Android Java
In questo esempio completo mostriamo diversi passaggi consecutivi: anzitutto, selezione di un’immagine dalla galleria; successivamente, upload su Firebase Storage; quindi, recupero dell’URL di download; infine, salvataggio dell’URL nel documento utente su Firestore.
- Selezione di un’immagine dalla galleria.
- Upload su Firebase Storage.
- Recupero dell’URL di download.
- Salvataggio dell’URL nel documento utente su Firestore.
Per realizzare questo esempio sono necessari i seguenti prerequisiti:
- Utente autenticato (con Firebase Authentication).
- Firestore configurato nel progetto.
public class ProfileActivity extends AppCompatActivity {
private static final int REQUEST_CODE_PROFILE_IMAGE = 2001;
private FirebaseStorage storage;
private StorageReference storageRef;
private FirebaseFirestore db;
private FirebaseAuth auth;
private ImageView imageViewProfile;
private ProgressBar progressBar;
private Uri selectedImageUri;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_profile);
storage = FirebaseStorage.getInstance();
storageRef = storage.getReference();
db = FirebaseFirestore.getInstance();
auth = FirebaseAuth.getInstance();
imageViewProfile = findViewById(R.id.imageViewProfile);
progressBar = findViewById(R.id.progressBarProfile);
imageViewProfile.setOnClickListener(v -> openImagePicker());
}
private void openImagePicker() {
Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
intent.setType("image/*");
startActivityForResult(Intent.createChooser(intent, "Seleziona immagine profilo"),
REQUEST_CODE_PROFILE_IMAGE);
}
@Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == REQUEST_CODE_PROFILE_IMAGE && resultCode == RESULT_OK && data != null) {
selectedImageUri = data.getData();
if (selectedImageUri != null) {
imageViewProfile.setImageURI(selectedImageUri);
uploadProfileImage();
}
}
}
private void uploadProfileImage() {
FirebaseUser currentUser = auth.getCurrentUser();
if (currentUser == null || selectedImageUri == null) {
return;
}
progressBar.setVisibility(View.VISIBLE);
String userId = currentUser.getUid();
String path = "profile_images/" + userId + ".jpg";
StorageReference profileRef = storageRef.child(path);
profileRef.putFile(selectedImageUri)
.addOnSuccessListener(taskSnapshot -> profileRef.getDownloadUrl()
.addOnSuccessListener(uri -> {
String downloadUrl = uri.toString();
// Salva URL nel documento utente su Firestore
db.collection("users")
.document(userId)
.update("profileImageUrl", downloadUrl)
.addOnSuccessListener(unused -> {
progressBar.setVisibility(View.GONE);
Toast.makeText(this, "Immagine profilo aggiornata", Toast.LENGTH_SHORT).show();
})
.addOnFailureListener(e -> {
progressBar.setVisibility(View.GONE);
Toast.makeText(this, "Errore salvataggio URL: " + e.getMessage(),
Toast.LENGTH_SHORT).show();
});
})
.addOnFailureListener(e -> {
progressBar.setVisibility(View.GONE);
Toast.makeText(this, "Errore nel recupero URL: " + e.getMessage(),
Toast.LENGTH_SHORT).show();
}))
.addOnFailureListener(e -> {
progressBar.setVisibility(View.GONE);
Toast.makeText(this, "Errore upload: " + e.getMessage(),
Toast.LENGTH_SHORT).show();
});
}
}
Best practices per Firebase Storage su Android
- Organizza i percorsi: usa una struttura chiara (es.
users/<uid>/profile.jpg,posts/<postId>/images/<file>). - Metadati: se necessario, salva metadati utili (tipo MIME, autore, timestamp) in Firestore invece di sovraccaricare Firebase Storage.
- Dimensione file: evita di caricare file troppo grandi da mobile. Comprimi immagini, riduci la risoluzione, limita la dimensione massima da UI.
- Regole di sicurezza: non lasciare le regole in modalità test. Restringi l’accesso in base all’utente (UID) e al percorso del file.
- Gestione rete: informa l’utente se la connessione è assente o lenta. Puoi usare il
ConnectivityManagerper controllare lo stato della connessione. - Retry: Firebase Storage gestisce già alcuni retry automatici, ma per operazioni critiche puoi implementare un tuo meccanismo di ripetizione.
- UI reattiva: mostra sempre una progress bar o un indicatore durante upload/download lunghi.
- Pulizia file inutilizzati: elimina i file orfani quando cancelli i dati associati nel database (ad es. quando un utente cancella il proprio account o un post).
Rispondi