Recentemente ho avuto la necessità di servire da web un'intera struttura di filesystem contenente file e directory.
La soluzione più logica che ho trovato è stata quella di zippare tutto il contenuto del mio pacchetto e servirlo via web al client.
Di seguito il metodo che ho scritto per realizzare questa funzionalità:
/**
* Zippa ricorsivamente un filesystem.
* <b>Le directory vuote vengono ignorate.</b>
* @param absolutePath Il path radice del mio fs.
* Lo utilizzo per salvare nello zip i file con il nome
* relativo e non assoluto.
* @param workingDir Il path da processare.
* @param zipOutputStream Lo stream dello zip da creare.
* @throws GenericException In caso di errori di processing.
*/
public static void prepareZipFile(String absolutePath,
String workingDir, ZipOutputStream zipOutputStream)
throws Exception {
try {
// Creo un oggetto File della directory che voglio zippare
File zipDir = new File(workingDir);
// Recupero tutti i file della mia directory
String[] dirList = zipDir.list();
// Preparo il buffer per la lettura dei file
byte[] readBuffer = new byte[2156];
int bytesIn = 0;
// Ciclo sul contenuto della directory e zippo ogni file
for (int i=0;dirList != null && i < dirList.length; i++) {
File f = new File(zipDir, dirList[i]);
if (f.isDirectory()) {
// Se il file in questione è una directory
// richiamo me stesso ricorsivamente
String filePath = f.getPath();
prepareZipFile(absolutePath, filePath, zipOutputStream);
// continuo il ciclo
continue;
}
// Se arriviamo qui il nostro file non è una directory
FileInputStream fis = new FileInputStream(f);
// Creo una ZipEntry
// Attenzione! Il nome ed il percorso che darò qui
// sarà il nome ed il percorso che il file avrà all'interno dello zip
// Nel mio caso voglio che il percorso nello zip sia relativo, quindi
// sottraggo la radice dal nome del file
ZipEntry anEntry =
new ZipEntry(f.getPath().substring(absolutePath.length()));
// notifico allo zip che stò per inserire un file
zipOutputStream.putNextEntry(anEntry);
// inserisco il byte[] nello zip
while ((bytesIn = fis.read(readBuffer)) != -1) {
zipOutputStream.write(readBuffer, 0, bytesIn);
}
// notifico allo zip che ho finito lo stream dell'entry
zipOutputStream.closeEntry();
// chiudo lo stream del file
fis.close();
}
} catch(Exception e) {
// Gestisco l'eccezione
}
}
Dalla mia action / servlet, mi basterà richiamare questo metodo così:
// Header e nome file..
response.setContentType("application/zip");
response.setHeader("Content-Disposition", "attachment;filename=myZip-" +
new SimpleDateFormat("yyyyMMddHHmm").format(new java.util.Date()) + ".zip");
// Creo lo stream dello zip redirigendolo direttamente verso il client
ZipOutputStream zipFile =
new ZipOutputStream(response.getOutputStream());
// Richiamo il mio metodo per zippare
MyUtils.prepareZipFile(tempDir.getAbsolutePath(),
tempDir.getAbsolutePath(), zipFile);
// Completate le operazioni non dimenticatevi di chiudere lo zip
zipFile.close();
Fine! Il risultato sarà un file zip contenente tutta la vostra struttura di directory!
Enjoy!