Creating folder unzipping epub in Android - java

I have read a lot of threads on this site regarding my issue, and the code I have is based off some of them. The problem I have currently is that unzipping an ePub results in only the mimetype file being extracted with LogCat getting errors in creating a folder from what I understand.
Following is what I use to decompress any zip file, I got it from Problem when Unzipping:
/**
* #author jon
*/
public class Decompress {
private String _zipFile;
private String _location;
ZipEntry ze = null;
public Decompress(String zipFile, String location) {
_zipFile = zipFile;
_location = location;
_dirChecker("");
}
public void unzip() {
try {
FileInputStream fin = new FileInputStream(_zipFile);
ZipInputStream zin = new ZipInputStream(fin);
while ((ze = zin.getNextEntry()) != null) {
Log.v("Decompress", "Unzipping " + ze.getName());
if (ze.isDirectory()) {
_dirChecker(ze.getName());
} else {
FileOutputStream fout = new FileOutputStream(_location
+ ze.getName());
for (int c = zin.read(); c != -1; c = zin.read()) {
fout.write(c);
}
zin.closeEntry();
fout.close();
}
}
zin.close();
} catch (Exception e) {
Log.e("Decompress", "unzip", e);
}
}
private void _dirChecker(String dir) {
File f = new File(_location + dir);
Log.d("Decompress", f.getAbsolutePath().toString());
if (!f.isDirectory()) {
f.mkdirs();
}
}
}
Other articles I have referred to:
Unzip a zipped file on sd card in Android application
Why my decompress class don't make directories?
Uzip folders recursively -android
Render epub files in android
Android EPUBLIB read/load content
Here is the LogCat Error I receive on the second file(/folder) after the mimetype file.
03-24 10:38:13.991: E/Decompress(23190): java.io.FileNotFoundException: /storage/emulated/0/unzipped/yamani/CDS-suggestions.docx: open failed: ENOENT (No such file or directory)
I'm currently using a regular zip file to test out my code.
Thanks for the help.

Was able to solve my problem by using another snippet found on this site.
Source:
public class unpackZip {
public boolean unzip(String zipname, String path) {
InputStream is;
ZipInputStream zis;
try {
String filename;
is = new FileInputStream(zipname);
zis = new ZipInputStream(new BufferedInputStream(is));
ZipEntry ze;
byte[] buffer = new byte[1024];
int count;
while ((ze = zis.getNextEntry()) != null) {
// zapis do souboru
filename = ze.getName();
// Need to create directories if not exists, or
// it will generate an Exception...
if (ze.isDirectory()) {
File fmd = new File(path, filename);
fmd.mkdirs();
continue;
} else {
// Make this part of the code more efficient .code-revise
File fmd = new File(path, filename);
Log.d("Unzipping", fmd.getParentFile().getPath());
String parent = fmd.getParentFile().getPath();
File fmd_1 = new File(parent);
fmd_1.mkdirs();
// end of .code-revise
}
FileOutputStream fout = new FileOutputStream(path + filename);
// cteni zipu a zapis
while ((count = zis.read(buffer)) != -1) {
fout.write(buffer, 0, count);
}
fout.close();
zis.closeEntry();
}
zis.close();
} catch (IOException e) {
e.printStackTrace();
return false;
}
return true;
}
}

Related

How to implement Parallel Zip Creation with ScatterZipOutputStream with Zip64 Support?

I am wondering if any one can help implementing Parallel Zip Creation using ScatterZipOutputStream . I have searched a lot but no where I am finding an example for the same.
https://commons.apache.org/proper/commons-compress/zip.html
I have tried making Zip, Zipping a directory etc with ZipArchiveOutputStream . Now, I am trying to do that in parallel.
public static void makeZip(String filename) throws IOException,
ArchiveException {
File sourceFile = new File(filename);
final OutputStream out = new FileOutputStream(filename.substring(0, filename.lastIndexOf('.')) + ".zip");
ZipArchiveOutputStream os = new ZipArchiveOutputStream(out);
os.setUseZip64(Zip64Mode.AsNeeded);
os.putArchiveEntry(new ZipArchiveEntry(sourceFile.getName()));
IOUtils.copy(new FileInputStream(sourceFile), os);
os.closeArchiveEntry();
os.close();
}
It should be able to process individual files as thread and then combine it to write the result zip.
Following is the working code of both zip and unzip:
1. Change path for sourceFolder and zipFilePath
2. Zipping only *.text type of files it can be any type or all the files
3. Unzipped files at sourceFolder/unzip/
Import following dependency in build.gradle or in pom.xml
implementation("org.apache.commons:commons-compress:1.18")
implementation("commons-io:commons-io:2.6")
Ref: https://mvnrepository.com/artifact/org.apache.commons/commons-compress/1.18
https://mvnrepository.com/artifact/commons-io/commons-io/2.6
//code
import org.apache.commons.compress.archivers.zip.*;
import org.apache.commons.compress.parallel.InputStreamSupplier;
import org.apache.commons.io.FileUtils;
import java.io.*;
import java.nio.file.Files;
import java.util.Iterator;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
public class ZipMain {
static ParallelScatterZipCreator scatterZipCreator = new ParallelScatterZipCreator();
static ScatterZipOutputStream dirs;
static {
try {
dirs = ScatterZipOutputStream.fileBased(File.createTempFile("java-zip-dirs", "tmp"));
} catch (IOException e) {
e.printStackTrace();
}
}
public static void main(String[] args) throws IOException {
String sourceFolder = "/Users/<user>/Desktop/";
String zipFilePath = "/Users/<user>/Desktop/Desk.zip";
String fileTypesToBeAddedToZip = "txt";
zip(sourceFolder, zipFilePath, fileTypesToBeAddedToZip);
unzip(zipFilePath, sourceFolder + "/unzip/");
}
private static void zip(String sourceFolder, String zipFilePath, String fileTypesToBeAddedToZip) throws IOException {
OutputStream outputStream = null;
ZipArchiveOutputStream zipArchiveOutputStream = null;
try {
File srcFolder = new File(sourceFolder);
if (srcFolder.isDirectory()) {
// uncomment following code if you want to add all files under srcFolder
//Iterator<File> fileIterator = Arrays.asList(srcFolder.listFiles()).iterator();
Iterator<File> fileIterator = FileUtils.iterateFiles(srcFolder, new String[]{fileTypesToBeAddedToZip}, true);
File zipFile = new File(zipFilePath);
zipFile.delete();
outputStream = new FileOutputStream(zipFile);
zipArchiveOutputStream = new ZipArchiveOutputStream(outputStream);
zipArchiveOutputStream.setUseZip64(Zip64Mode.AsNeeded);
int srcFolderLength = srcFolder.getAbsolutePath().length() + 1; // +1 to remove the last file separator
while (fileIterator.hasNext()) {
File file = fileIterator.next();
// uncomment following code if you want to add all files under srcFolder
//if (file.isDirectory()) {
// continue;
// }
String relativePath = file.getAbsolutePath().substring(srcFolderLength);
InputStreamSupplier streamSupplier = () -> {
InputStream is = null;
try {
is = Files.newInputStream(file.toPath());
} catch (IOException e) {
e.printStackTrace();
}
return is;
};
ZipArchiveEntry zipArchiveEntry = new ZipArchiveEntry(relativePath);
zipArchiveEntry.setMethod(ZipEntry.DEFLATED);
scatterZipCreator.addArchiveEntry(zipArchiveEntry, streamSupplier);
}
scatterZipCreator.writeTo(zipArchiveOutputStream);
}
if (zipArchiveOutputStream != null) {
zipArchiveOutputStream.close();
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if (outputStream != null) {
outputStream.close();
}
}
}
private static void unzip(String zipFilePath, String destDir) {
File dir = new File(destDir);
// create output directory if it doesn't exist
if (!dir.exists()) {
dir.mkdirs();
} else {
dir.delete();
}
FileInputStream fis;
//buffer for read and write data to file
byte[] buffer = new byte[1024];
try {
fis = new FileInputStream(zipFilePath);
ZipInputStream zis = new ZipInputStream(fis);
ZipEntry ze = zis.getNextEntry();
while (ze != null) {
String fileName = ze.getName();
File newFile = new File(destDir + File.separator + fileName);
System.out.println("Unzipping to " + newFile.getAbsolutePath());
//create directories for sub directories in zip
String parentFolder = newFile.getParent();
File folder = new File(parentFolder);
folder.mkdirs();
FileOutputStream fos = new FileOutputStream(newFile);
int len;
while ((len = zis.read(buffer)) > 0) {
fos.write(buffer, 0, len);
}
fos.close();
//close this ZipEntry
zis.closeEntry();
ze = zis.getNextEntry();
}
//close last ZipEntry
zis.closeEntry();
zis.close();
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
Ref: Fast zipping folder using java ParallelScatterZipCreator

How do I create a zipfile from a non-physical file?

I have a project called "MyApp". MyApp will use a java library I am creating called "MyLibrary". I am writing a function in "MyLibrary" to unzip a zip file in "MyApp"(or whatever app is using "MyLibrary") "resources" dir.
Reading https://community.oracle.com/blogs/kohsuke/2007/04/25/how-convert-javaneturl-javaiofile I cannot create a File via a path because it is not a "physical file". I was using zip4j, but its constructor takes a File or String rather than an InputStream. So I cannot do this:
ZipFile zipfile = new
ZipFile("src/main/resources/compressed.zip");
downloadedZipfile.extractAll("src/main/resources");
java.io.File javadoc and http://www.mkyong.com/java/how-to-convert-inputstream-to-file-in-java/ indicate there isn't a way to convert an InputStream to a File.
Is there another way to access the zip file in the project that is using my library? Thank you in advance.
UPDATE:
The zipIn lacks entries, so the while loop won't extract the file out.
InputStream in = getInputStream("", JSON_FILENAME);
ZipInputStream zipIn = new ZipInputStream(in);
ZipEntry entry;
try {
while ((entry = zipIn.getNextEntry()) != null) {
String filepath = entry.getName();
if(!entry.isDirectory()) {
extractFile(zipIn, filepath);
}
else {
File dir = new File(filepath);
dir.mkdir();
}
zipIn.closeEntry();
}
zipIn.close();
} catch (IOException e) {
e.printStackTrace();
}
private void extractFile(ZipInputStream zipIn, String filepath) {
BufferedOutputStream bos = null;
try {
bos = new BufferedOutputStream(new FileOutputStream(filepath));
} catch (FileNotFoundException e) {
e.printStackTrace();
}
byte[] bytesIn = new byte[BUFFER_SIZE];
int read = 0;
try {
while ((read = zipIn.read(bytesIn)) != -1) {
bos.write(bytesIn, 0, read);
}
bos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
Will the file be extracted into the home directory of "MyApp" when using this code that belongs to "MyLibrary"?
If you have a InputStream to your virtual ZIP file you can use java.util.zip.ZipInputStream to read the ZIP entries:
InputStream in = ...
ZipInputStream zipIn = new ZipInputStream(in);
ZipEntry entry;
while ((entry = zipIn.getNextEntry()) != null) {
// handle the entry
}

Unzip to folder in internal storage

I'm trying to create a folder in internal storage called "unzip" and then unzip a file into the internal storage "unzip folder". Not sure what i'm doing wrong? If you could please explain what's wrong it'd be great! Thanks
Edit: The issues is that I don't think the folder is being created and the file is also not being unzipped.
public void send(View view) {
try {
File mydir = this.getDir("unzip", Context.MODE_PRIVATE);//create folder in internal storage
unzip(getFilesDir().getAbsolutePath(), job_no, getFilesDir() + "/unzip/");
} catch (IOException e) {
}
}
public void unzip(String filepath, String filename, String unzip_path) throws IOException {
InputStream is = new FileInputStream(filepath + filename);
ZipInputStream zis = new ZipInputStream(new BufferedInputStream(is));
try {
ZipEntry ze;
while ((ze = zis.getNextEntry()) != null) {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
int count;
String filename_temp = ze.getName();
File fmd = new File(filepath + filename_temp);
if (!fmd.getParentFile().exists()) {
fmd.getParentFile().mkdirs();
}
FileOutputStream fout = new FileOutputStream(unzip_path + filename_temp);
while ((count = zis.read(buffer)) != -1) {
baos.write(buffer, 0, count);
byte[] bytes = baos.toByteArray();
fout.write(bytes);
baos.reset();
}
fout.close();
//}
}
} finally {
zis.close();
}
}
This will get absolute storage device.
final static String MEDIA_PATH = Environment.getExternalStorageDirectory().getPath() + "/";

How to unzip files programmatically in Android?

I need a small code snippet which unzips a few files from a given .zip file and gives the separate files according to the format they were in the zipped file. Please post your knowledge and help me out.
Had peno's version optimised a bit. The increase in performance is perceptible.
private boolean unpackZip(String path, String zipname)
{
InputStream is;
ZipInputStream zis;
try
{
String filename;
is = new FileInputStream(path + zipname);
zis = new ZipInputStream(new BufferedInputStream(is));
ZipEntry ze;
byte[] buffer = new byte[1024];
int count;
while ((ze = zis.getNextEntry()) != null)
{
filename = ze.getName();
// Need to create directories if not exists, or
// it will generate an Exception...
if (ze.isDirectory()) {
File fmd = new File(path + filename);
fmd.mkdirs();
continue;
}
FileOutputStream fout = new FileOutputStream(path + filename);
while ((count = zis.read(buffer)) != -1)
{
fout.write(buffer, 0, count);
}
fout.close();
zis.closeEntry();
}
zis.close();
}
catch(IOException e)
{
e.printStackTrace();
return false;
}
return true;
}
Based on Vasily Sochinsky's answer a bit tweaked & with a small fix:
public static void unzip(File zipFile, File targetDirectory) throws IOException {
ZipInputStream zis = new ZipInputStream(
new BufferedInputStream(new FileInputStream(zipFile)));
try {
ZipEntry ze;
int count;
byte[] buffer = new byte[8192];
while ((ze = zis.getNextEntry()) != null) {
File file = new File(targetDirectory, ze.getName());
File dir = ze.isDirectory() ? file : file.getParentFile();
if (!dir.isDirectory() && !dir.mkdirs())
throw new FileNotFoundException("Failed to ensure directory: " +
dir.getAbsolutePath());
if (ze.isDirectory())
continue;
FileOutputStream fout = new FileOutputStream(file);
try {
while ((count = zis.read(buffer)) != -1)
fout.write(buffer, 0, count);
} finally {
fout.close();
}
/* if time should be restored as well
long time = ze.getTime();
if (time > 0)
file.setLastModified(time);
*/
}
} finally {
zis.close();
}
}
Notable differences
public static - this is a static utility method that can be anywhere.
2 File parameters because String are :/ for files and one could not specify where the zip file is to be extracted before. Also path + filename concatenation > https://stackoverflow.com/a/412495/995891
throws - because catch late - add a try catch if really not interested in them.
actually makes sure that the required directories exist in all cases. Not every zip contains all the required directory entries in advance of file entries. This had 2 potential bugs:
if the zip contains an empty directory and instead of the resulting directory there is an existing file, this was ignored. The return value of mkdirs() is important.
could crash on zip files that don't contain directories.
increased write buffer size, this should improve performance a bit. Storage is usually in 4k blocks and writing in smaller chunks is usually slower than necessary.
uses the magic of finally to prevent resource leaks.
So
unzip(new File("/sdcard/pictures.zip"), new File("/sdcard"));
should do the equivalent of the original
unpackZip("/sdcard/", "pictures.zip")
This is my unzip method, which I use:
private boolean unpackZip(String path, String zipname)
{
InputStream is;
ZipInputStream zis;
try
{
is = new FileInputStream(path + zipname);
zis = new ZipInputStream(new BufferedInputStream(is));
ZipEntry ze;
while((ze = zis.getNextEntry()) != null)
{
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
int count;
String filename = ze.getName();
FileOutputStream fout = new FileOutputStream(path + filename);
// reading and writing
while((count = zis.read(buffer)) != -1)
{
baos.write(buffer, 0, count);
byte[] bytes = baos.toByteArray();
fout.write(bytes);
baos.reset();
}
fout.close();
zis.closeEntry();
}
zis.close();
}
catch(IOException e)
{
e.printStackTrace();
return false;
}
return true;
}
The Kotlin way
//FileExt.kt
data class ZipIO (val entry: ZipEntry, val output: File)
fun File.unzip(unzipLocationRoot: File? = null) {
val rootFolder = unzipLocationRoot ?: File(parentFile.absolutePath + File.separator + nameWithoutExtension)
if (!rootFolder.exists()) {
rootFolder.mkdirs()
}
ZipFile(this).use { zip ->
zip
.entries()
.asSequence()
.map {
val outputFile = File(rootFolder.absolutePath + File.separator + it.name)
ZipIO(it, outputFile)
}
.map {
it.output.parentFile?.run{
if (!exists()) mkdirs()
}
it
}
.filter { !it.entry.isDirectory }
.forEach { (entry, output) ->
zip.getInputStream(entry).use { input ->
output.outputStream().use { output ->
input.copyTo(output)
}
}
}
}
}
Usage
val zipFile = File("path_to_your_zip_file")
file.unzip()
Android has build-in Java API. Check out java.util.zip package.
The class ZipInputStream is what you should look into. Read ZipEntry from the ZipInputStream and dump it into filesystem/folder. Check similar example to compress into zip file.
use the following class
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import android.util.Log;
public class DecompressFast {
private String _zipFile;
private String _location;
public DecompressFast(String zipFile, String location) {
_zipFile = zipFile;
_location = location;
_dirChecker("");
}
public void unzip() {
try {
FileInputStream fin = new FileInputStream(_zipFile);
ZipInputStream zin = new ZipInputStream(fin);
ZipEntry ze = null;
while ((ze = zin.getNextEntry()) != null) {
Log.v("Decompress", "Unzipping " + ze.getName());
if(ze.isDirectory()) {
_dirChecker(ze.getName());
} else {
FileOutputStream fout = new FileOutputStream(_location + ze.getName());
BufferedOutputStream bufout = new BufferedOutputStream(fout);
byte[] buffer = new byte[1024];
int read = 0;
while ((read = zin.read(buffer)) != -1) {
bufout.write(buffer, 0, read);
}
bufout.close();
zin.closeEntry();
fout.close();
}
}
zin.close();
Log.d("Unzip", "Unzipping complete. path : " +_location );
} catch(Exception e) {
Log.e("Decompress", "unzip", e);
Log.d("Unzip", "Unzipping failed");
}
}
private void _dirChecker(String dir) {
File f = new File(_location + dir);
if(!f.isDirectory()) {
f.mkdirs();
}
}
}
How to use
String zipFile = Environment.getExternalStorageDirectory() + "/the_raven.zip"; //your zip file location
String unzipLocation = Environment.getExternalStorageDirectory() + "/unzippedtestNew/"; // destination folder location
DecompressFast df= new DecompressFast(zipFile, unzipLocation);
df.unzip();
Permissions
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
While the answers that are already here work well, I found that they were slightly slower than I had hoped for. Instead I used zip4j, which I think is the best solution because of its speed. It also allowed for different options for the amount of compression, which I found useful.
According to #zapl answer,Unzip with progress report:
public interface UnzipFile_Progress
{
void Progress(int percent, String FileName);
}
// unzip(new File("/sdcard/pictures.zip"), new File("/sdcard"));
public static void UnzipFile(File zipFile, File targetDirectory, UnzipFile_Progress progress) throws IOException,
FileNotFoundException
{
long total_len = zipFile.length();
long total_installed_len = 0;
ZipInputStream zis = new ZipInputStream(new BufferedInputStream(new FileInputStream(zipFile)));
try
{
ZipEntry ze;
int count;
byte[] buffer = new byte[1024];
while ((ze = zis.getNextEntry()) != null)
{
if (progress != null)
{
total_installed_len += ze.getCompressedSize();
String file_name = ze.getName();
int percent = (int)(total_installed_len * 100 / total_len);
progress.Progress(percent, file_name);
}
File file = new File(targetDirectory, ze.getName());
File dir = ze.isDirectory() ? file : file.getParentFile();
if (!dir.isDirectory() && !dir.mkdirs())
throw new FileNotFoundException("Failed to ensure directory: " + dir.getAbsolutePath());
if (ze.isDirectory())
continue;
FileOutputStream fout = new FileOutputStream(file);
try
{
while ((count = zis.read(buffer)) != -1)
fout.write(buffer, 0, count);
} finally
{
fout.close();
}
// if time should be restored as well
long time = ze.getTime();
if (time > 0)
file.setLastModified(time);
}
} finally
{
zis.close();
}
}
public class MainActivity extends Activity {
private String LOG_TAG = MainActivity.class.getSimpleName();
private File zipFile;
private File destination;
private TextView status;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
status = (TextView) findViewById(R.id.main_status);
status.setGravity(Gravity.CENTER);
if ( initialize() ) {
zipFile = new File(destination, "BlueBoxnew.zip");
try {
Unzipper.unzip(zipFile, destination);
status.setText("Extracted to \n"+destination.getAbsolutePath());
} catch (ZipException e) {
Log.e(LOG_TAG, e.getMessage());
} catch (IOException e) {
Log.e(LOG_TAG, e.getMessage());
}
} else {
status.setText("Unable to initialize sd card.");
}
}
public boolean initialize() {
boolean result = false;
File sdCard = new File(Environment.getExternalStorageDirectory()+"/zip/");
//File sdCard = Environment.getExternalStorageDirectory();
if ( sdCard != null ) {
destination = sdCard;
if ( !destination.exists() ) {
if ( destination.mkdir() ) {
result = true;
}
} else {
result = true;
}
}
return result;
}
}
->Helper Class(Unzipper.java)
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.ZipException;
import java.util.zip.ZipInputStream;
import android.util.Log;
public class Unzipper {
private static String LOG_TAG = Unzipper.class.getSimpleName();
public static void unzip(final File file, final File destination) throws ZipException, IOException {
new Thread() {
public void run() {
long START_TIME = System.currentTimeMillis();
long FINISH_TIME = 0;
long ELAPSED_TIME = 0;
try {
ZipInputStream zin = new ZipInputStream(new FileInputStream(file));
String workingDir = destination.getAbsolutePath()+"/";
byte buffer[] = new byte[4096];
int bytesRead;
ZipEntry entry = null;
while ((entry = zin.getNextEntry()) != null) {
if (entry.isDirectory()) {
File dir = new File(workingDir, entry.getName());
if (!dir.exists()) {
dir.mkdir();
}
Log.i(LOG_TAG, "[DIR] "+entry.getName());
} else {
FileOutputStream fos = new FileOutputStream(workingDir + entry.getName());
while ((bytesRead = zin.read(buffer)) != -1) {
fos.write(buffer, 0, bytesRead);
}
fos.close();
Log.i(LOG_TAG, "[FILE] "+entry.getName());
}
}
zin.close();
FINISH_TIME = System.currentTimeMillis();
ELAPSED_TIME = FINISH_TIME - START_TIME;
Log.i(LOG_TAG, "COMPLETED in "+(ELAPSED_TIME/1000)+" seconds.");
} catch (Exception e) {
Log.e(LOG_TAG, "FAILED");
}
};
}.start();
}
}
->xml layout(activity_main.xml):
<RelativeLayout 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"
tools:context=".MainActivity" >
<TextView
android:id="#+id/main_status"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_centerVertical="true"
android:text="#string/hello_world" />
</RelativeLayout>
->permission in Menifest file:
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
Here is a ZipFileIterator (like a java Iterator, but for zip files):
package ch.epfl.bbp.io;
import java.io.BufferedInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Iterator;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
public class ZipFileIterator implements Iterator<File> {
private byte[] buffer = new byte[1024];
private FileInputStream is;
private ZipInputStream zis;
private ZipEntry ze;
public ZipFileIterator(File file) throws FileNotFoundException {
is = new FileInputStream(file);
zis = new ZipInputStream(new BufferedInputStream(is));
}
#Override
public boolean hasNext() {
try {
return (ze = zis.getNextEntry()) != null;
} catch (IOException e) {
e.printStackTrace();
}
return false;
}
#Override
public File next() {
try {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
int count;
String filename = ze.getName();
File tmpFile = File.createTempFile(filename, "tmp");
tmpFile.deleteOnExit();// TODO make it configurable
FileOutputStream fout = new FileOutputStream(tmpFile);
while ((count = zis.read(buffer)) != -1) {
baos.write(buffer, 0, count);
byte[] bytes = baos.toByteArray();
fout.write(bytes);
baos.reset();
}
fout.close();
zis.closeEntry();
return tmpFile;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
#Override
public void remove() {
throw new RuntimeException("not implemented");
}
public void close() {
try {
zis.close();
is.close();
} catch (IOException e) {// nope
}
}
}
Minimal example I used to unzip a specific file from my zipfile into my applications cache folder. I then read the manifest file using a different method.
private void unzipUpdateToCache() {
ZipInputStream zipIs = new ZipInputStream(context.getResources().openRawResource(R.raw.update));
ZipEntry ze = null;
try {
while ((ze = zipIs.getNextEntry()) != null) {
if (ze.getName().equals("update/manifest.json")) {
FileOutputStream fout = new FileOutputStream(context.getCacheDir().getAbsolutePath() + "/manifest.json");
byte[] buffer = new byte[1024];
int length = 0;
while ((length = zipIs.read(buffer))>0) {
fout.write(buffer, 0, length);
}
zipIs .closeEntry();
fout.close();
}
}
zipIs .close();
} catch (IOException e) {
e.printStackTrace();
}
}
I'm working with zip files which Java's ZipFile class isn't able to handle. Java 8 apparently can't handle compression method 12 (bzip2 I believe). After trying a number of methods including zip4j (which also fails with these particular files due to another issue), I had success with Apache's commons-compress which supports additional compression methods as mentioned here.
Note that the ZipFile class below is not the one from java.util.zip.
It's actually org.apache.commons.compress.archivers.zip.ZipFile so be careful with the imports.
try (ZipFile zipFile = new ZipFile(archiveFile)) {
Enumeration<ZipArchiveEntry> entries = zipFile.getEntries();
while (entries.hasMoreElements()) {
ZipArchiveEntry entry = entries.nextElement();
File entryDestination = new File(destination, entry.getName());
if (entry.isDirectory()) {
entryDestination.mkdirs();
} else {
entryDestination.getParentFile().mkdirs();
try (InputStream in = zipFile.getInputStream(entry); OutputStream out = new FileOutputStream(entryDestination)) {
IOUtils.copy(in, out);
}
}
}
} catch (IOException ex) {
log.debug("Error unzipping archive file: " + archiveFile, ex);
}
For Gradle:
compile 'org.apache.commons:commons-compress:1.18'
Based on zapl's answer, adding try() around Closeable's closes the streams automatically after use.
public static void unzip(File zipFile, File targetDirectory) {
try (FileInputStream fis = new FileInputStream(zipFile)) {
try (BufferedInputStream bis = new BufferedInputStream(fis)) {
try (ZipInputStream zis = new ZipInputStream(bis)) {
ZipEntry ze;
int count;
byte[] buffer = new byte[Constant.DefaultBufferSize];
while ((ze = zis.getNextEntry()) != null) {
File file = new File(targetDirectory, ze.getName());
File dir = ze.isDirectory() ? file : file.getParentFile();
if (!dir.isDirectory() && !dir.mkdirs())
throw new FileNotFoundException("Failed to ensure directory: " + dir.getAbsolutePath());
if (ze.isDirectory())
continue;
try (FileOutputStream fout = new FileOutputStream(file)) {
while ((count = zis.read(buffer)) != -1)
fout.write(buffer, 0, count);
}
}
}
}
} catch (Exception ex) {
//handle exception
}
}
Using Constant.DefaultBufferSize (65536) gotten from C# .NET 4 Stream.CopyTo from Jon Skeet's answer here:
https://stackoverflow.com/a/411605/1876355
I always just see posts using byte[1024] or byte[4096] buffer, never knew it can be much larger which improves performance and is still working perfectly normal.
Here is the Stream Source code:
https://referencesource.microsoft.com/#mscorlib/system/io/stream.cs
//We pick a value that is the largest multiple of 4096 that is still smaller than the large object heap threshold (85K).
// The CopyTo/CopyToAsync buffer is short-lived and is likely to be collected at Gen0, and it offers a significant
// improvement in Copy performance.
private const int _DefaultCopyBufferSize = 81920;
However, I dialed it back to 65536 which is also a multiple of 4096 just to be safe.
Password Protected Zip File
if you want to compress files with password you can take a look at this library that can zip files with password easily:
Zip:
ZipArchive zipArchive = new ZipArchive();
zipArchive.zip(targetPath,destinationPath,password);
Unzip:
ZipArchive zipArchive = new ZipArchive();
zipArchive.unzip(targetPath,destinationPath,password);
Rar:
RarArchive rarArchive = new RarArchive();
rarArchive.extractArchive(file archive, file destination);
The documentation of this library is good enough, I just added a few examples from there.
It's totally free and wrote specially for android.
Here is more concise version of #arsent solution:
fun File.unzip(to: File? = null) {
val destinationDir = to ?: File(parentFile, nameWithoutExtension)
destinationDir.mkdirs()
ZipFile(this).use { zipFile ->
zipFile
.entries()
.asSequence()
.filter { !it.isDirectory }
.forEach { zipEntry ->
val currFile = File(destinationDir, zipEntry.name)
currFile.parentFile?.mkdirs()
zipFile.getInputStream(zipEntry).use { input ->
currFile.outputStream().use { output -> input.copyTo(output) }
}
}
}
}

How to create a zip file in Java

I have a dynamic text file that picks content from a database according to the user's query. I have to write this content into a text file and zip it in a folder in a servlet. How should I do this?
Look at this example:
StringBuilder sb = new StringBuilder();
sb.append("Test String");
File f = new File("d:\\test.zip");
ZipOutputStream out = new ZipOutputStream(new FileOutputStream(f));
ZipEntry e = new ZipEntry("mytext.txt");
out.putNextEntry(e);
byte[] data = sb.toString().getBytes();
out.write(data, 0, data.length);
out.closeEntry();
out.close();
This will create a zip in the root of D: named test.zip which will contain one single file called mytext.txt. Of course you can add more zip entries and also specify a subdirectory like this:
ZipEntry e = new ZipEntry("folderName/mytext.txt");
You can find more information about compression with Java here.
Java 7 has ZipFileSystem built in, that can be used to create, write and read file from zip file.
Java Doc: ZipFileSystem Provider
Map<String, String> env = new HashMap<>();
// Create the zip file if it doesn't exist
env.put("create", "true");
URI uri = URI.create("jar:file:/codeSamples/zipfs/zipfstest.zip");
try (FileSystem zipfs = FileSystems.newFileSystem(uri, env)) {
Path externalTxtFile = Paths.get("/codeSamples/zipfs/SomeTextFile.txt");
Path pathInZipfile = zipfs.getPath("/SomeTextFile.txt");
// Copy a file into the zip file
Files.copy(externalTxtFile, pathInZipfile, StandardCopyOption.REPLACE_EXISTING);
}
To write a ZIP file, you use a ZipOutputStream. For each entry that you want to place into the ZIP file, you create a ZipEntry object. You pass the file name to the ZipEntry constructor; it sets the other parameters such as file date and decompression method. You can override these settings if you like. Then, you call the putNextEntry method of the ZipOutputStream to begin writing a new file. Send the file data to the ZIP stream. When you are done, call closeEntry. Repeat for all the files you want to store. Here is a code skeleton:
FileOutputStream fout = new FileOutputStream("test.zip");
ZipOutputStream zout = new ZipOutputStream(fout);
for all files
{
ZipEntry ze = new ZipEntry(filename);
zout.putNextEntry(ze);
send data to zout;
zout.closeEntry();
}
zout.close();
Here is an example code to compress a Whole Directory(including sub files and sub directories), it's using the walk file tree feature of Java NIO.
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.file.*;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
public class ZipCompress {
public static void compress(String dirPath) {
final Path sourceDir = Paths.get(dirPath);
String zipFileName = dirPath.concat(".zip");
try {
final ZipOutputStream outputStream = new ZipOutputStream(new FileOutputStream(zipFileName));
Files.walkFileTree(sourceDir, new SimpleFileVisitor<Path>() {
#Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attributes) {
try {
Path targetFile = sourceDir.relativize(file);
outputStream.putNextEntry(new ZipEntry(targetFile.toString()));
byte[] bytes = Files.readAllBytes(file);
outputStream.write(bytes, 0, bytes.length);
outputStream.closeEntry();
} catch (IOException e) {
e.printStackTrace();
}
return FileVisitResult.CONTINUE;
}
});
outputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
To use this, just call
ZipCompress.compress("target/directoryToCompress");
and you'll get a zip file directoryToCompress.zip
Single file:
String filePath = "/absolute/path/file1.txt";
String zipPath = "/absolute/path/output.zip";
try (ZipOutputStream zipOut = new ZipOutputStream(new FileOutputStream(zipPath))) {
File fileToZip = new File(filePath);
zipOut.putNextEntry(new ZipEntry(fileToZip.getName()));
Files.copy(fileToZip.toPath(), zipOut);
}
Multiple files:
List<String> filePaths = Arrays.asList("/absolute/path/file1.txt", "/absolute/path/file2.txt");
String zipPath = "/absolute/path/output.zip";
try (ZipOutputStream zipOut = new ZipOutputStream(new FileOutputStream(zipPath))) {
for (String filePath : filePaths) {
File fileToZip = new File(filePath);
zipOut.putNextEntry(new ZipEntry(fileToZip.getName()));
Files.copy(fileToZip.toPath(), zipOut);
}
}
Spring boot controller, zip the files in a directory, and can be downloaded.
#RequestMapping(value = "/files.zip")
#ResponseBody
byte[] filesZip() throws IOException {
File dir = new File("./");
File[] filesArray = dir.listFiles();
if (filesArray == null || filesArray.length == 0)
System.out.println(dir.getAbsolutePath() + " have no file!");
ByteArrayOutputStream bo = new ByteArrayOutputStream();
ZipOutputStream zipOut= new ZipOutputStream(bo);
for(File xlsFile:filesArray){
if(!xlsFile.isFile())continue;
ZipEntry zipEntry = new ZipEntry(xlsFile.getName());
zipOut.putNextEntry(zipEntry);
zipOut.write(IOUtils.toByteArray(new FileInputStream(xlsFile)));
zipOut.closeEntry();
}
zipOut.close();
return bo.toByteArray();
}
This is how you create a zip file from a source file:
String srcFilename = "C:/myfile.txt";
String zipFile = "C:/myfile.zip";
try {
byte[] buffer = new byte[1024];
FileOutputStream fos = new FileOutputStream(zipFile);
ZipOutputStream zos = new ZipOutputStream(fos);
File srcFile = new File(srcFilename);
FileInputStream fis = new FileInputStream(srcFile);
zos.putNextEntry(new ZipEntry(srcFile.getName()));
int length;
while ((length = fis.read(buffer)) > 0) {
zos.write(buffer, 0, length);
}
zos.closeEntry();
fis.close();
zos.close();
}
catch (IOException ioe) {
System.out.println("Error creating zip file" + ioe);
}
public static void main(String args[])
{
omtZip("res/", "omt.zip");
}
public static void omtZip(String path,String outputFile)
{
final int BUFFER = 2048;
boolean isEntry = false;
ArrayList<String> directoryList = new ArrayList<String>();
File f = new File(path);
if(f.exists())
{
try {
FileOutputStream fos = new FileOutputStream(outputFile);
ZipOutputStream zos = new ZipOutputStream(new BufferedOutputStream(fos));
byte data[] = new byte[BUFFER];
if(f.isDirectory())
{
//This is Directory
do{
String directoryName = "";
if(directoryList.size() > 0)
{
directoryName = directoryList.get(0);
System.out.println("Directory Name At 0 :"+directoryName);
}
String fullPath = path+directoryName;
File fileList = null;
if(directoryList.size() == 0)
{
//Main path (Root Directory)
fileList = f;
}else
{
//Child Directory
fileList = new File(fullPath);
}
String[] filesName = fileList.list();
int totalFiles = filesName.length;
for(int i = 0 ; i < totalFiles ; i++)
{
String name = filesName[i];
File filesOrDir = new File(fullPath+name);
if(filesOrDir.isDirectory())
{
System.out.println("New Directory Entry :"+directoryName+name+"/");
ZipEntry entry = new ZipEntry(directoryName+name+"/");
zos.putNextEntry(entry);
isEntry = true;
directoryList.add(directoryName+name+"/");
}else
{
System.out.println("New File Entry :"+directoryName+name);
ZipEntry entry = new ZipEntry(directoryName+name);
zos.putNextEntry(entry);
isEntry = true;
FileInputStream fileInputStream = new FileInputStream(filesOrDir);
BufferedInputStream bufferedInputStream = new BufferedInputStream(fileInputStream, BUFFER);
int size = -1;
while( (size = bufferedInputStream.read(data, 0, BUFFER)) != -1 )
{
zos.write(data, 0, size);
}
bufferedInputStream.close();
}
}
if(directoryList.size() > 0 && directoryName.trim().length() > 0)
{
System.out.println("Directory removed :"+directoryName);
directoryList.remove(0);
}
}while(directoryList.size() > 0);
}else
{
//This is File
//Zip this file
System.out.println("Zip this file :"+f.getPath());
FileInputStream fis = new FileInputStream(f);
BufferedInputStream bis = new BufferedInputStream(fis,BUFFER);
ZipEntry entry = new ZipEntry(f.getName());
zos.putNextEntry(entry);
isEntry = true;
int size = -1 ;
while(( size = bis.read(data,0,BUFFER)) != -1)
{
zos.write(data, 0, size);
}
}
//CHECK IS THERE ANY ENTRY IN ZIP ? ----START
if(isEntry)
{
zos.close();
}else
{
zos = null;
System.out.println("No Entry Found in Zip");
}
//CHECK IS THERE ANY ENTRY IN ZIP ? ----START
}catch(Exception e)
{
e.printStackTrace();
}
}else
{
System.out.println("File or Directory not found");
}
}
}
Given exportPath and queryResults as String variables, the following block creates a results.zip file under exportPath and writes the content of queryResults to a results.txt file inside the zip.
URI uri = URI.create("jar:file:" + exportPath + "/results.zip");
Map<String, String> env = Collections.singletonMap("create", "true");
try (FileSystem zipfs = FileSystems.newFileSystem(uri, env)) {
Path filePath = zipfs.getPath("/results.txt");
byte[] fileContent = queryResults.getBytes();
Files.write(filePath, fileContent, StandardOpenOption.CREATE);
}
You have mainly to create two functions. First is writeToZipFile() and second is createZipfileForOutPut .... and then call the createZipfileForOutPut('file name of .zip')` …
public static void writeToZipFile(String path, ZipOutputStream zipStream)
throws FileNotFoundException, IOException {
System.out.println("Writing file : '" + path + "' to zip file");
File aFile = new File(path);
FileInputStream fis = new FileInputStream(aFile);
ZipEntry zipEntry = new ZipEntry(path);
zipStream.putNextEntry(zipEntry);
byte[] bytes = new byte[1024];
int length;
while ((length = fis.read(bytes)) >= 0) {
zipStream.write(bytes, 0, length);
}
zipStream.closeEntry();
fis.close();
}
public static void createZipfileForOutPut(String filename) {
String home = System.getProperty("user.home");
// File directory = new File(home + "/Documents/" + "AutomationReport");
File directory = new File("AutomationReport");
if (!directory.exists()) {
directory.mkdir();
}
try {
FileOutputStream fos = new FileOutputStream("Path to your destination" + filename + ".zip");
ZipOutputStream zos = new ZipOutputStream(fos);
writeToZipFile("Path to file which you want to compress / zip", zos);
zos.close();
fos.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
There is another option by using zip4j at https://github.com/srikanth-lingala/zip4j
Creating a zip file with single file in it / Adding single file to an existing zip
new ZipFile("filename.zip").addFile("filename.ext");
Or
new ZipFile("filename.zip").addFile(new File("filename.ext"));
Creating a zip file with multiple files / Adding multiple files to an existing zip
new ZipFile("filename.zip").addFiles(Arrays.asList(new File("first_file"), new File("second_file")));
Creating a zip file by adding a folder to it / Adding a folder to an existing zip
new ZipFile("filename.zip").addFolder(new File("/user/myuser/folder_to_add"));
Creating a zip file from stream / Adding a stream to an existing zip
new ZipFile("filename.zip").addStream(inputStream, new ZipParameters());
I know this question is answered but if you have a list of strings and you want to create a separate file for each string in the archive, you can use the snippet below.
public void zipFileTest() throws IOException {
Map<String, String> map = Map.ofEntries(
new AbstractMap.SimpleEntry<String, String>("File1.txt", "File1 Content"),
new AbstractMap.SimpleEntry<String, String>("File2.txt", "File2 Content"),
new AbstractMap.SimpleEntry<String, String>("File3.txt", "File3 Content")
);
createZipFileFromStringContents(map, "archive.zip");
}
public void createZipFileFromStringContents(Map<String, String> map, String zipfilePath) throws IOException {
FileOutputStream fout = new FileOutputStream(zipfilePath);
ZipOutputStream zout = new ZipOutputStream(fout);
for (Map.Entry<String, String> entry : map.entrySet()) {
String fileName = entry.getKey();
ZipEntry zipFile = new ZipEntry(fileName);
zout.putNextEntry(zipFile);
String fileContent = entry.getValue();
zout.write(fileContent.getBytes(), 0, fileContent.getBytes().length);
zout.closeEntry();
}
zout.close();
}
It will create a zip file with the structure as in the below image:
Here is my working solution:
public static byte[] createZipFile(Map<String, FileData> files) throws IOException {
try(ByteArrayOutputStream tZipFile = new ByteArrayOutputStream()) {
try (ZipOutputStream tZipFileOut = new ZipOutputStream(tZipFile)) {
for (Map.Entry<String, FileData> file : files.entrySet()) {
ZipEntry zipEntry = new ZipEntry(file.getValue().getFileName());
tZipFileOut.putNextEntry(zipEntry);
tZipFileOut.write(file.getValue().getBytes());
}
}
return tZipFile.toByteArray();
}
}
public class FileData {
private String fileName;
private byte[] bytes;
public String getFileName() {
return this.fileName;
}
public byte[] getBytes() {
return this.bytes;
}
}
This will create byte[] of ZIP file which contains one or more compressed files. I've used this method inside controller method and write bytes[] of ZIP file into response to download ZIP file(s) from server.
Since it took me a while to figure it out, I thought it would be helpful to post my solution using Java 7+ ZipFileSystem
openZip(runFile);
addToZip(filepath); //loop construct;
zipfs.close();
private void openZip(File runFile) throws IOException {
Map<String, String> env = new HashMap<>();
env.put("create", "true");
env.put("encoding", "UTF-8");
Files.deleteIfExists(runFile.toPath());
zipfs = FileSystems.newFileSystem(URI.create("jar:" + runFile.toURI().toString()), env);
}
private void addToZip(String filename) throws IOException {
Path externalTxtFile = Paths.get(filename).toAbsolutePath();
Path pathInZipfile = zipfs.getPath(filename.substring(filename.lastIndexOf("results"))); //all files to be stored have a common base folder, results/ in my case
if (Files.isDirectory(externalTxtFile)) {
Files.createDirectories(pathInZipfile);
try (DirectoryStream<Path> ds = Files.newDirectoryStream(externalTxtFile)) {
for (Path child : ds) {
addToZip(child.normalize().toString()); //recursive call
}
}
} else {
// copy file to zip file
Files.copy(externalTxtFile, pathInZipfile, StandardCopyOption.REPLACE_EXISTING);
}
}
public static void zipFromTxt(String zipFilePath, String txtFilePath) {
Assert.notNull(zipFilePath, "Zip file path is required");
Assert.notNull(txtFilePath, "Txt file path is required");
zipFromTxt(new File(zipFilePath), new File(txtFilePath));
}
public static void zipFromTxt(File zipFile, File txtFile) {
ZipOutputStream out = null;
FileInputStream in = null;
try {
Assert.notNull(zipFile, "Zip file is required");
Assert.notNull(txtFile, "Txt file is required");
out = new ZipOutputStream(new FileOutputStream(zipFile));
in = new FileInputStream(txtFile);
out.putNextEntry(new ZipEntry(txtFile.getName()));
int len;
byte[] buffer = new byte[1024];
while ((len = in.read(buffer)) > 0) {
out.write(buffer, 0, len);
out.flush();
}
} catch (Exception e) {
log.info("Zip from txt occur error,Detail message:{}", e.toString());
} finally {
try {
if (in != null) in.close();
if (out != null) {
out.closeEntry();
out.close();
}
} catch (Exception e) {
log.info("Zip from txt close error,Detail message:{}", e.toString());
}
}
}
Using Jeka https://jeka.dev JkPathTree, it's quite straightforward.
Path wholeDirToZip = Paths.get("dir/to/zip");
Path zipFile = Paths.get("file.zip");
JkPathTree.of(wholeDirToZip).zipTo(zipFile);
If you want decompress without software better use this code. Other code with pdf files sends error on manually decompress
byte[] buffer = new byte[1024];
try {
FileOutputStream fos = new FileOutputStream("123.zip");
ZipOutputStream zos = new ZipOutputStream(fos);
ZipEntry ze= new ZipEntry("file.pdf");
zos.putNextEntry(ze);
FileInputStream in = new FileInputStream("file.pdf");
int len;
while ((len = in.read(buffer)) > 0) {
zos.write(buffer, 0, len);
}
in.close();
zos.closeEntry();
zos.close();
} catch(IOException ex) {
ex.printStackTrace();
}

Categories

Resources