i'm facing a problem with unzipping files in Android. Here is the code snippet:
public void unzip() {
try {
FileInputStream fin = new FileInputStream(_zipFile);
BufferedInputStream in = new BufferedInputStream(fin);
ZipInputStream zin = new ZipInputStream(in);
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 out = new BufferedOutputStream(fout);
byte[] buffer = new byte[1024];
int length;
while ((length = zin.read(buffer,0,1024)) >= 0) {
out.write(buffer,0,length);
}
/* while ((length = zin.read(buffer))>0) {
out.write(buffer, 0, length);
}*/
/*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);
}
}
Smaller files (smaller than 10kB) are unzipped like empty - size 0 (html files, .jpg). Other files are ok. If I use this same code, but without buffers all the files are ok - ofcourse, unzipping without buffers is out of the question since it runs too long. Files are stored on SD card on real device. I have already tried setting smaller buffer size ( even new byte[2]). Thanks in advance...
Try this code instead,
public void doUnzip(String inputZipFile, String destinationDirectory)
throws IOException {
int BUFFER = 2048;
List zipFiles = new ArrayList();
File sourceZipFile = new File(inputZip);
File unzipDestinationDirectory = new File(destinationDirectory);
unzipDestinationDirectory.mkdir();
ZipFile zipFile;
// Open Zip file for reading
zipFile = new ZipFile(sourceZipFile, ZipFile.OPEN_READ);
// Create an enumeration of the entries in the zip file
Enumeration zipFileEntries = zipFile.entries();
// Process each entry
while (zipFileEntries.hasMoreElements()) {
// grab a zip file entry
ZipEntry entry = (ZipEntry) zipFileEntries.nextElement();
String currentEntry = entry.getName();
File destFile = new File(unzipDestinationDirectory, currentEntry);
// destFile = new File(unzipDestinationDirectory, destFile.getName());
if (currentEntry.endsWith(".zip")) {
zipFiles.add(destFile.getAbsolutePath());
}
// grab file's parent directory structure
File destinationParent = destFile.getParentFile();
// create the parent directory structure if needed
destinationParent.mkdirs();
try {
// extract file if not a directory
if (!entry.isDirectory()) {
BufferedInputStream is =
new BufferedInputStream(zipFile.getInputStream(entry));
int currentByte;
// establish buffer for writing file
byte data[] = new byte[BUFFER];
// write the current file to disk
FileOutputStream fos = new FileOutputStream(destFile);
BufferedOutputStream dest =
new BufferedOutputStream(fos, BUFFER);
// read and write until last byte is encountered
while ((currentByte = is.read(data, 0, BUFFER)) != -1) {
dest.write(data, 0, currentByte);
}
dest.flush();
dest.close();
is.close();
}
} catch (IOException ioe) {
ioe.printStackTrace();
}
}
zipFile.close();
for (Iterator iter = zipFiles.iterator(); iter.hasNext();) {
String zipName = (String)iter.next();
doUnzip(
zipName,
destinationDirectory +
File.separatorChar +
zipName.substring(0,zipName.lastIndexOf(".zip"))
);
}
}
Related
I am getting the "Zip Path Traversal Vulnerability" alert in Google Play Console.
I followed official Google docs (https://support.google.com/faqs/answer/9294009) to fix it but the alert is still there.
Here is the code that handles unzipping. I have even tested using a zip file that has the vulnerability described and the exception is raised as expected.
What am I missing?
private boolean unpackZip(File zipFile, File outputDirectory, IOnResult<Integer> progress) {
InputStream is;
ZipInputStream zis;
ZipInputStream zisCount;
try {
int totalEntries = 0;
int entryCount = 0;
String filename;
ZipEntry ze;
is = new FileInputStream(zipFile);
zisCount = new ZipInputStream(new BufferedInputStream(is));
while ((ze = zisCount.getNextEntry()) != null) {
// Fixing a Zip Path Traversal Vulnerability
// (https://support.google.com/faqs/answer/9294009)
filename = ze.getName();
File targetFile = new File(outputDirectory, filename);
String targetPath = targetFile.getCanonicalPath();
if (!targetPath.startsWith(outputDirectory.getCanonicalPath())) {
throw new SecurityException("Archive security error");
}
// -----------------------------------------------------------------------------
totalEntries++;
zisCount.closeEntry();
}
zisCount.close();
is = new FileInputStream(zipFile);
zis = new ZipInputStream(new BufferedInputStream(is));
byte[] buffer = new byte[1024];
int count;
while ((ze = zis.getNextEntry()) != null) {
filename = ze.getName();
// Fixing a Zip Path Traversal Vulnerability (https://support.google.com/faqs/answer/9294009)
File targetFile = new File(outputDirectory, filename);
String targetPath = targetFile.getCanonicalPath();
if (!targetPath.startsWith(outputDirectory.getCanonicalPath())) {
throw new SecurityException("Archive security error");
}
// Need to create directories if not exists, or
// it will generate an Exception...
if (ze.isDirectory()) {
File fmd = new File(outputDirectory, filename);
fmd.mkdirs();
continue;
}
File outputFile = new File(outputDirectory, filename);
for( File parentFile = outputFile.getParentFile(); !parentFile.exists(); parentFile = parentFile.getParentFile() )
{
parentFile.mkdir();
}
FileOutputStream fout = new FileOutputStream(outputFile);
Log.d(TAG, "unzipped " + filename);
while ((count = zis.read(buffer)) != -1) {
fout.write(buffer, 0, count);
}
fout.close();
zis.closeEntry();
entryCount++;
}
zis.close();
} catch (IOException | SecurityException e) {
Log.e(TAG, "unpackZip", e);
return false;
}
return true;
}
I want to unzip to zip file in same directory,When I try below code it unzipped below target directory not in target folder,how can I unzipped all files in target directory in android
public static void unzip(String zipFilePath, String destDir) {
BufferedOutputStream bufferedOutputStream = null;
FileInputStream fileInputStream;
File dest_file = new File(destDir);
//dest_file.mkdirs(); // creates if destination directory not existed
try {
fileInputStream = new FileInputStream(zipFilePath);
ZipInputStream zipInputStream = new ZipInputStream(new BufferedInputStream(fileInputStream));
ZipEntry zipEntry;
while ((zipEntry = zipInputStream.getNextEntry()) != null) {
String zipEntryName = zipEntry.getName();
File file = new File(destDir + zipEntryName);
if (file.exists()) {
} else if (zipEntry.isDirectory()) {
file.mkdirs();
} else {
byte buffer[] = new byte[1024];
FileOutputStream fileOutputStream = new FileOutputStream(file);
bufferedOutputStream = new BufferedOutputStream(fileOutputStream, 1024);
int count;
while ((count = zipInputStream.read(buffer, 0, 1024)) != -1) {
bufferedOutputStream.write(buffer, 0, count);
}
bufferedOutputStream.flush();
bufferedOutputStream.close();
}
}
zipInputStream.close();
} catch (Exception e) {
Log.e("Decompress", "unzip", e);
}
}
I call unzip File
String source = /storage/emulated/0/Android/data/com.kocsistem.pixageoneandroid/files/Contents/widgetContent + "/" + "123.zip";
String target = /storage/emulated/0/Android/data/com.kocsistem.pixageoneandroid/files/Contents/widgetContent ;
Utils.unzip(source, target);
I have put in place the ability to archive user data for my application by creating a compressed file.
I create the compressed file like this :
try
{
int iBufferSize = 2048;
int iByteCount = 0;
byte btData[] = new byte[iBufferSize];
File fZipFile = new File(ARCHIVE_FILE);
FileOutputStream fos = new FileOutputStream(fZipFile);
ZipOutputStream zos = new ZipOutputStream(fos);
BufferedInputStream bis = null;
FileInputStream fIn = null;
ZipEntry ze = null;
File[] fPartitions = new File(FILES_PATH).listFiles();
for(File fPartition : fPartitions)
{
lFileSize = fPartition.length();
fIn = new FileInputStream(fPartition);
bis = new BufferedInputStream(fIn, iBufferSize);
ze = new ZipEntry(fPartition.getName());
ze.setSize(lFileSize);
zos.putNextEntry(ze);
while((iByteCount = bis.read(btData, 0, iBufferSize)) != -1)
{
zos.write(btData, 0, iByteCount);
}
bis.close();
zos.closeEntry();
}
zos.close();
bis.close();
fIn.close();
bOk = true;
}
catch(Exception e)
{
e.printStackTrace();
}
Now, when I try to restore the contents, I am not able to get the original file size. Here is how I am doing the expansion :
try
{
// open the archive
FileInputStream fis = new FileInputStream(fArchive);
ZipInputStream zis = new ZipInputStream(new BufferedInputStream(fis));
bBuffer = new byte[2048];
// iterate through all files, putting them back into the same place
ze = zis.getNextEntry();
while(ze != null)
{
strFileName = ze.getName();
strFileName = FILE_PATH + "/" + strFileName;
fCheck = new File(strFileName);
lZipFileSize = ze.getSize(); // <--- returns -1 systematically
lTargetFileSize = fCheck.length();
fCheck = null;
FileOutputStream fos = new FileOutputStream(strFileName);
// read the data
while((iCount = zis.read(bBuffer)) != -1)
{
fos.write(bBuffer, 0, iCount);
}
fos.close();
ze = zis.getNextEntry();
}
zis.close();
fis.close();
bOk = true;
}
catch(Exception e)
{
e.printStackTrace();
}
I have noted that ze.getSize() is returning -1 systematically.
How can I store and get the individual file sizes ?
Ok, so if you come across this problem, here is how I worked around it.
Use the ZipFile class for reading the zip file.
Inspired from ZipInputStream: getting file size of -1 when reading and The ZipFileTest.java Android example source code
I have several zip files that each contains several files too which I want to extract using the ZipInputStream class. Among them are some images. When I try to extract these images using the BufferedOutputStream they are decompressed partially and the images are incomplete.
private void extractArchives() {
ZipInputStream zis;
File archiveDir = new File(
Environment.getExternalStorageDirectory().getAbsolutePath() +
"/archives/");
File[] files = archiveDir.listFiles();
for (int i = 0; i < files.length; ++i)
{
File file = files[i];
try
{
zis = new ZipInputStream(new FileInputStream(file));
ZipEntry ze;
while ((ze = zis.getNextEntry()) != null)
{
BufferedOutputStream bos;
byte[] buffer = new byte[102400];
int count;
while ((count = zis.read(buffer)) != -1)
{
String fileName = ze.getName();
if (fileName.endsWith(".jpg"))
{
path += File.separator + fileName;
bos = new BufferedOutputStream(new FileOutputStream(path));
bos.write(buffer, 0, count);
bos.close();
}
}
}
zis.close();
}
catch(FileNotFoundException e) { continue; }
//If the file is not a zip file or is a directory
catch (IOException e) { continue; }
}
}
Is there anything wrong with the code above? Does using BufferedOutputStream cause this problem? I appreciate any ideas. Thanks.
I modified the method according to what Erwin said and now it works:
private void extractArchives() {
File archiveDir = new File(
Environment.getExternalStorageDirectory().getAbsolutePath() +
"/archives/");
String archivePath = archiveDir.getAbsolutePath();
File[] files = archiveDir.listFiles();
for (int i = 0; i < files.length; ++i)
{
File file = files[i];
if(!file.isDirectory())
{
try {
ZipInputStream zis = new ZipInputStream(new FileInputStream(file));
ZipEntry entry = zis.getNextEntry();
while (entry != null)
{
if(entry.getName().endsWith(".jpg"))
{
String imagePath = themePath + File.separator + entry.getName();
BufferedOutputStream bos = new BufferedOutputStream(
new FileOutputStream(imagePath));
byte[] buffer = new byte[4096];
int read = 0;
while ((read = zis.read(buffer)) != -1) bos.write(buffer, 0, read);
imagePath = "";
bos.close();
}
zis.closeEntry();
entry = zis.getNextEntry();
}
zis.close();
}
catch (FileNotFoundException e) {}
catch (IOException e) {}
}
}
}
Focusing on the loop that iterates over the zip entries, the problem is that you were reading at most 102400 bytes from a zip entry and then writing that to a new file. The next maximum 102400 bytes if the same image file was written to a new file. If the path name was the same as the old file, it would overwrite the old file. But since you were constantly appending to the local variable 'path', I'm not sure where the data ended up.
Pull the opening of the FileOutputStream out of the inner loop, and keep writing to the same OutputStream until you can read no more bytes from the current ZipEntry. Only then move on to the next ZipEntry and to the next OutputStream.
This is a standard pattern to read and copy between streams in Java so I can fix your code without having the definitions of the files, path and zis but it would help if you can post an actual compiling code example so that other people can benefit from this too.
Another improvement you need in production code, is to wrap bos.close() in a try/finally block so that the output file is also closed if there was an exception while reading from the input.
while ((ze = zis.getNextEntry()) != null) {
String fileName = ze.getName();
if (fileName.endsWith(".jpg")) {
String filepath = path + File.separator + fileName;
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(filepath));
byte[] buffer = new byte[102400];
int count;
while ((count = zis.read(buffer)) != -1) {
bos.write(buffer, 0, count);
}
bos.close();
}
}
my app downloads a zip with about 350 files. A mix of JPG and HTML files. The function i wrote to do it works just fine but the unzipping takes for ever.
At first i thought the reason might be that writing to the sd-card is slow. but when i unzip the same zip with an other app on my phone it works much faster. is there anything that i could do to optimize it?
here is the code:
private void extract() {
try {
FileInputStream inStream = new FileInputStream(targetFilePath);
ZipInputStream zipStream = new ZipInputStream(new BufferedInputStream(inStream));
ZipEntry entry;
ZipFile zip = new ZipFile(targetFilePath);
//i know the contents for the zip so i create the dirs i need in advance
new File(targetFolder).mkdirs();
new File(targetFolder + "META-INF").mkdir();
new File(targetFolder + "content").mkdir();
int extracted = 0;
while((entry = zipStream.getNextEntry()) != null) {
if (entry.isDirectory()) {
new File(targetFolder + entry.getName()).mkdirs();
} else {
FileOutputStream outStream = new FileOutputStream(targetFolder + entry.getName());
for (int c = zipStream.read(); c != -1; c = zipStream.read()) {
outStream.write(c);
}
zipStream.closeEntry();
outStream.close();
extracted ++;
}
publishProgress(""+(int)extracted*100/zip.size());
}
zipStream.close();
inStream.close();
//
new File(targetFilePath).delete();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
thanks to CommonsWare i modified my code like this:
int size;
byte[] buffer = new byte[2048];
FileOutputStream outStream = new FileOutputStream(targetFolder + entry.getName());
BufferedOutputStream bufferOut = new BufferedOutputStream(outStream, buffer.length);
while((size = zipStream.read(buffer, 0, buffer.length)) != -1) {
bufferOut.write(buffer, 0, size);
}
bufferOut.flush();
bufferOut.close();
big performance difference.
Thanks a lot.
You are reading and writing a byte at a time. Consider reading and writing a larger block at a time.
Just use this method once and believe me its a super fast process.. It will unzip all the files without skipping any file with in 1 second.
public boolean rajDhaniSuperFastUnzip(String inputZipFile, String destinationDirectory)
{
try {
int BUFFER = 2048;
List<String> zipFiles = new ArrayList<String>();
File sourceZipFile = new File(inputZipFile);
File unzipDestinationDirectory = new File(destinationDirectory);
unzipDestinationDirectory.mkdir();
ZipFile zipFile;
zipFile = new ZipFile(sourceZipFile, ZipFile.OPEN_READ);
Enumeration<?> zipFileEntries = zipFile.entries();
while (zipFileEntries.hasMoreElements()) {
ZipEntry entry = (ZipEntry) zipFileEntries.nextElement();
String currentEntry = entry.getName();
File destFile = new File(unzipDestinationDirectory, currentEntry);
if (currentEntry.endsWith(".zip")) {
zipFiles.add(destFile.getAbsolutePath());
}
File destinationParent = destFile.getParentFile();
destinationParent.mkdirs();
try {
if (!entry.isDirectory()) {
BufferedInputStream is =
new BufferedInputStream(zipFile.getInputStream(entry));
int currentByte;
byte data[] = new byte[BUFFER];
FileOutputStream fos = new FileOutputStream(destFile);
BufferedOutputStream dest =
new BufferedOutputStream(fos, BUFFER);
while ((currentByte = is.read(data, 0, BUFFER)) != -1) {
dest.write(data, 0, currentByte);
}
dest.flush();
dest.close();
is.close();
}
} catch (IOException ioe) {
ioe.printStackTrace();
}
}
zipFile.close();
for (Iterator<String> iter = zipFiles.iterator(); iter.hasNext();) {
String zipName = (String)iter.next();
doUnzip(
zipName,
destinationDirectory +
File.separatorChar +
zipName.substring(0,zipName.lastIndexOf(".zip"))
);
}
} catch (IOException e) {
e.printStackTrace();
return false ;
}
return true;
}
Hope this will help you.. Happy Coding :)