I need to zip up a "project" folder to allow users to share projects via email. I found a class for zipping up multiple files into one zip, but I need to keep the folder structure in my zip. Is there any way to achieve this on android? Thanks in advance.
-
To clarify, do you mean the Android development project or a project that is developed in app? – Dan S Jul 13 '11 at 18:28
-
Project that is developed in the app, sorry about that. Also I think this is what I need: http://stackoverflow.com/questions/1399126/java-util-zip-recreating-directory-structure but when I copied it, I tried my best, but couldn't find out how to the the line: Deque
queue = new LinkedList – Mark Jul 15 '11 at 14:45(); to work. I know Deque is an interface and LinkedList implements it, but eclipes just keeps giving me errors. -
1Nevermind, found out how to do it after a lot of searching: http://www.crazysquirrel.com/computing/java/basics/java-directory-zipping.jspx – Mark Jul 15 '11 at 16:07
8 Answers
This code should do the trick.
Note: you must add file write permissions to your app by adding the WRITE_EXTERNAL_STORAGE permission to your manifest.xml file.
/*
*
* Zips a file at a location and places the resulting zip file at the toLocation
* Example: zipFileAtPath("downloads/myfolder", "downloads/myFolder.zip");
*/
public boolean zipFileAtPath(String sourcePath, String toLocation) {
final int BUFFER = 2048;
File sourceFile = new File(sourcePath);
try {
BufferedInputStream origin = null;
FileOutputStream dest = new FileOutputStream(toLocation);
ZipOutputStream out = new ZipOutputStream(new BufferedOutputStream(
dest));
if (sourceFile.isDirectory()) {
zipSubFolder(out, sourceFile, sourceFile.getParent().length());
} else {
byte data[] = new byte[BUFFER];
FileInputStream fi = new FileInputStream(sourcePath);
origin = new BufferedInputStream(fi, BUFFER);
ZipEntry entry = new ZipEntry(getLastPathComponent(sourcePath));
entry.setTime(sourceFile.lastModified()); // to keep modification time after unzipping
out.putNextEntry(entry);
int count;
while ((count = origin.read(data, 0, BUFFER)) != -1) {
out.write(data, 0, count);
}
}
out.close();
} catch (Exception e) {
e.printStackTrace();
return false;
}
return true;
}
/*
*
* Zips a subfolder
*
*/
private void zipSubFolder(ZipOutputStream out, File folder,
int basePathLength) throws IOException {
final int BUFFER = 2048;
File[] fileList = folder.listFiles();
BufferedInputStream origin = null;
for (File file : fileList) {
if (file.isDirectory()) {
zipSubFolder(out, file, basePathLength);
} else {
byte data[] = new byte[BUFFER];
String unmodifiedFilePath = file.getPath();
String relativePath = unmodifiedFilePath
.substring(basePathLength);
FileInputStream fi = new FileInputStream(unmodifiedFilePath);
origin = new BufferedInputStream(fi, BUFFER);
ZipEntry entry = new ZipEntry(relativePath);
entry.setTime(file.lastModified()); // to keep modification time after unzipping
out.putNextEntry(entry);
int count;
while ((count = origin.read(data, 0, BUFFER)) != -1) {
out.write(data, 0, count);
}
origin.close();
}
}
}
/*
* gets the last path component
*
* Example: getLastPathComponent("downloads/example/fileToZip");
* Result: "fileToZip"
*/
public String getLastPathComponent(String filePath) {
String[] segments = filePath.split("/");
if (segments.length == 0)
return "";
String lastPathComponent = segments[segments.length - 1];
return lastPathComponent;
}
-
Hey @HailZeon! Great code, and really helpful. What does getLastPathComponent(sourcePath) do and how is it defined? Thanks! – RaymondMachira Aug 07 '13 at 05:00
-
Hey @RaymondMachira. I added the definition for getLastPathComponent. It basically takes a path ("folder1/subfolder/example.txt") and returns "folder1/subfolder/". The zip file might not have the containing folder defined within it so we need to strip the sub path out and add it. – HailZeon Aug 12 '13 at 06:43
-
@HailZeon -I used the below code snippet for android, and I could get the zip file on my device but when I copy the same zip to windows desktop, it couldn't extract the zip, it says zip is invalid. Why is it so? – user1810931 Feb 27 '14 at 21:54
-
@HailZeon-I used the below code for android, i passed a folder path which i want to zip in sourcePath parameter and a folder path where i want to save the zip in toLocation paramenter. but it gives me exception at FileOutputStream dest = new FileOutputStream(toLocation); please tell me, how it's work. – Addicted Manish Mar 25 '14 at 07:30
-
@AddictedManish I updated the code so that you just have to copy and paste it. Please check that your sourcePath exists. Please also check that you have file write permissions in your manifest. – HailZeon Apr 01 '14 at 05:03
-
3I was getting empty named folder inside zip file, the fix was to replace sourceFile.getParent().length() with sourceFile.getParent().length()+1. – marcinj Jun 28 '14 at 13:00
-
1You should replace getLastPathComponent(String filePath) content by filePath.substring(filePath.lastIndexOf("/")) – Pdroid Jul 06 '15 at 20:24
-
2Great code - I am adding a fix for a bug I found - when trying to decompress on windows it fails due to parsing problem of the first "/" on the relative path given to the ZipEntry object. ===== code fix ==== String relativePath = unmodifiedFilePath .substring(basePathLength+1); – benchuk Oct 14 '15 at 07:29
-
@HailZeon Thanks for your answer, but i m getting this error... java.io.IOException: write failed: ENOSPC (No space left on device) Please help... – Anish Kumar Sep 24 '18 at 11:42
this is how I do it:
private static void zipFolder(String inputFolderPath, String outZipPath) {
try {
FileOutputStream fos = new FileOutputStream(outZipPath);
ZipOutputStream zos = new ZipOutputStream(fos);
File srcFile = new File(inputFolderPath);
File[] files = srcFile.listFiles();
Log.d("", "Zip directory: " + srcFile.getName());
for (int i = 0; i < files.length; i++) {
Log.d("", "Adding file: " + files[i].getName());
byte[] buffer = new byte[1024];
FileInputStream fis = new FileInputStream(files[i]);
zos.putNextEntry(new ZipEntry(files[i].getName()));
int length;
while ((length = fis.read(buffer)) > 0) {
zos.write(buffer, 0, length);
}
zos.closeEntry();
fis.close();
}
zos.close();
} catch (IOException ioe) {
Log.e("", ioe.getMessage());
}
}

- 5,511
- 4
- 32
- 53
-
-
1Zips the given folder. I zipped a folder full of images, the given folder got zipped, but the images inside the folder are not readable when unzipped. How to possibly fix it? – vss Apr 30 '19 at 15:05
-
If you want to unzip just add extension .zip at the end of the folder name, then you can easily extract it. – Abhishek kumar Feb 05 '21 at 10:14
I have reworked the code from HailZeon to work properly under windows. Zip Entries must be closed before new ones are started and a starting "/" at entry names like "/file.txt" makes also problems
/**
* Zips a Folder to "[Folder].zip"
* @param toZipFolder Folder to be zipped
* @return the resulting ZipFile
*/
public static File zipFolder(File toZipFolder) {
File ZipFile = new File(toZipFolder.getParent(), format("%s.zip", toZipFolder.getName()));
try {
ZipOutputStream out = new ZipOutputStream(new FileOutputStream(ZipFile));
zipSubFolder(out, toZipFolder, toZipFolder.getPath().length());
out.close();
return ZipFile;
} catch (Exception ex) {
ex.printStackTrace();
return null;
}
}
/**
* Main zip Function
* @param out Target ZipStream
* @param folder Folder to be zipped
* @param basePathLength Length of original Folder Path (for recursion)
*/
private static void zipSubFolder(ZipOutputStream out, File folder, int basePathLength) throws IOException {
final int BUFFER = 2048;
File[] fileList = folder.listFiles();
BufferedInputStream origin = null;
for (File file : fileList) {
if (file.isDirectory()) {
zipSubFolder(out, file, basePathLength);
} else {
byte data[] = new byte[BUFFER];
String unmodifiedFilePath = file.getPath();
String relativePath = unmodifiedFilePath.substring(basePathLength + 1);
FileInputStream fi = new FileInputStream(unmodifiedFilePath);
origin = new BufferedInputStream(fi, BUFFER);
ZipEntry entry = new ZipEntry(relativePath);
entry.setTime(file.lastModified()); // to keep modification time after unzipping
out.putNextEntry(entry);
int count;
while ((count = origin.read(data, 0, BUFFER)) != -1) {
out.write(data, 0, count);
}
origin.close();
out.closeEntry();
}
}
}

- 403
- 1
- 5
- 11
Use the zip4j library from this location. Import the jar file to your "app/libs/"
folder. And use the following code to zip your directories/files...
try {
File input = new File("path/to/your/input/fileOrFolder");
String destinationPath = Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator + "zippedItem.zip";
ZipParameters parameters = new ZipParameters();
parameters.setCompressionMethod(Zip4jConstants.COMP_STORE);
parameters.setCompressionLevel(Zip4jConstants.DEFLATE_LEVEL_NORMAL);
File output = new File(destinationPath);
ZipFile zipFile = new ZipFile(output);
// .addFolder or .addFile depending on your input
if (sourceFile.isDirectory())
zipFile.addFolder(input, parameters);
else
zipFile.addFile(input, parameters);
// Your input file/directory has been zipped at this point and you
// can access it as a normal file using the following line of code
File zippedFile = zipFile.getFile();
} catch (ZipException e) {
Log.e(TAG, Log.getStackTraceString(e));
}

- 1,551
- 1
- 21
- 17
public static boolean zip(File sourceFile, File zipFile) {
List<File> fileList = getSubFiles(sourceFile, true);
ZipOutputStream zipOutputStream = null;
try {
zipOutputStream = new ZipOutputStream(new FileOutputStream(zipFile));
int bufferSize = 1024;
byte[] buf = new byte[bufferSize];
ZipEntry zipEntry;
for(int i = 0; i < fileList.size(); i++) {
File file = fileList.get(i);
zipEntry = new ZipEntry(sourceFile.toURI().relativize(file.toURI()).getPath());
zipOutputStream.putNextEntry(zipEntry);
if (!file.isDirectory()) {
InputStream inputStream = new BufferedInputStream(new FileInputStream(file));
int readLength;
while ((readLength = inputStream.read(buf, 0, bufferSize)) != -1) {
zipOutputStream.write(buf, 0, readLength);
}
}
}
} catch (Exception e) {
e.printStackTrace();
return false;
} finally {
IoUtils.closeOS(zipOutputStream);
}
return true;
}
public static List<File> getSubFiles(File baseDir, boolean isContainFolder) {
List<File> fileList = new ArrayList<>();
File[] tmpList = baseDir.listFiles();
for (File file : tmpList) {
if (file.isFile()) {
fileList.add(file);
}
if (file.isDirectory()) {
if (isContainFolder) {
fileList.add(file); //key code
}
fileList.addAll(getSubFiles(file));
}
}
return fileList;
}

- 3,652
- 4
- 16
- 30
Just converting @HailZeon's answer to Kotlin:
import java.io.BufferedInputStream
import java.io.File
import java.io.FileInputStream
import java.io.FileOutputStream
import java.io.IOException
import java.util.zip.ZipEntry
import java.util.zip.ZipOutputStream
import java.util.zip.ZipException
private const val BUFFER = 2048
/**
* Compresses a file into a zip file
* @author Arnau Mora
* @since 20210318
* @param file The source file
* @param target The target file
*
* @throws NullPointerException If the entry name is null
* @throws IllegalArgumentException If the entry name is longer than 0xFFFF byte
* @throws SecurityException If a security manager exists and its SecurityManager.checkRead(String)
* method denies read access to the file
* @throws ZipException If a ZIP format error has occurred
* @throws IOException If an I/O error has occurre
*/
@Throws(
NullPointerException::class,
IllegalArgumentException::class,
SecurityException::class,
ZipException::class,
IOException::class
)
fun zipFile(file: File, target: File) {
val origin: BufferedInputStream
val dest: FileOutputStream
var zipOutput: ZipOutputStream? = null
try {
dest = target.outputStream()
zipOutput = ZipOutputStream(dest.buffered(BUFFER))
if (file.isDirectory)
zipSubFolder(zipOutput, file, file.parent!!.length)
else {
val data = ByteArray(BUFFER)
val fi = file.inputStream()
origin = fi.buffered(BUFFER)
val entry = ZipEntry(getLastPathComponent(file.path))
entry.time = file.lastModified()
zipOutput.putNextEntry(entry)
var count = origin.read(data, 0, BUFFER)
while (count != -1) {
zipOutput.write(data, 0, count)
count = origin.read(data, 0, BUFFER)
}
}
} finally {
zipOutput?.close()
}
}
private fun zipSubFolder(zipOutput: ZipOutputStream, folder: File, basePathLength: Int) {
val files = folder.listFiles() ?: return
var origin: BufferedInputStream? = null
try {
for (file in files) {
if (file.isDirectory)
zipSubFolder(zipOutput, folder, basePathLength)
else {
val data = ByteArray(BUFFER)
val unmodifiedFilePath = file.path
val relativePath = unmodifiedFilePath.substring(basePathLength)
val fi = FileInputStream(unmodifiedFilePath)
origin = fi.buffered(BUFFER)
val entry = ZipEntry(relativePath)
entry.time = file.lastModified()
zipOutput.putNextEntry(entry)
var count = origin.read(data, 0, BUFFER)
while (count != -1) {
zipOutput.write(data, 0, count)
count = origin.read(data, 0, BUFFER)
}
}
}
} finally {
origin?.close()
}
}
/*
* gets the last path component
*
* Example: getLastPathComponent("downloads/example/fileToZip");
* Result: "fileToZip"
*/
private fun getLastPathComponent(filePath: String): String {
val segments = filePath.split("/").toTypedArray()
return if (segments.isEmpty()) "" else segments[segments.size - 1]
}

- 5,784
- 5
- 18
- 32
If someone comes here trying to find a java8 cleaner refactored version of the @HailZeon code. Here it is:
private static final int BUFFER_SIZE = 2048;
public File zip(File source, String zipFileName) throws IOException {
File zipFile = null;
if(source != null) {
File zipFileDestination = new File(getCacheDir(), zipFileName);
if (zipFileDestination.exists()) {
zipFileDestination.delete();
}
zipFile = zip(source, zipFileDestination);
}
return zipFile;
}
public File zip(File source, File zipFile) throws IOException {
int relativeStartingPathIndex = zipFile.getAbsolutePath().lastIndexOf("/") + 1;
if (source != null && zipFile != null) {
try (ZipOutputStream out = new ZipOutputStream(new BufferedOutputStream( new FileOutputStream(zipFile)))) {
if (source.isDirectory()) {
zipSubDir(out, source, relativeStartingPathIndex);
} else {
try (BufferedInputStream origin = new BufferedInputStream(new FileInputStream(source))) {
zipEntryFile(origin, out, source, relativeStartingPathIndex);
}
}
}
}
return zipFile;
}
private void zipSubDir(ZipOutputStream out, File dir, int relativeStartingPathIndex) throws IOException {
File[] files = dir.listFiles();
if (files != null) {
for(File file : files) {
if(file.isDirectory()) {
zipSubDir(out, file, relativeStartingPathIndex);
} else {
try (BufferedInputStream origin = new BufferedInputStream(new FileInputStream(file))) {
zipEntryFile(origin, out, file, relativeStartingPathIndex);
}
}
}
}
}
private void zipEntryFile(BufferedInputStream origin, ZipOutputStream out, File file, int relativeStartingPathIndex) throws IOException {
String relativePath = file.getAbsolutePath().substring(relativeStartingPathIndex);
ZipEntry entry = new ZipEntry(relativePath);
entry.setTime(file.lastModified()); // to keep modification time after unzipping
out.putNextEntry(entry);
byte[] data = new byte[BUFFER_SIZE];
int count;
while ((count = origin.read(data, 0, BUFFER_SIZE)) != -1) {
out.write(data, 0, count);
}
}

- 1,526
- 1
- 17
- 28
If you use a java.util.zip object then you can write a script that does not modify the directory structure.

- 2,615
- 3
- 29
- 35