package org.rr.commons.utils.compression.zip;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.List;
import org.apache.commons.io.IOUtils;
import org.rr.commons.log.LoggerFactory;
import org.rr.commons.mufs.IResourceHandler;
import org.rr.commons.mufs.ResourceHandlerInputStream;
import org.rr.commons.utils.CommonUtils;
import org.rr.commons.utils.compression.CompressedDataEntry;
import org.rr.commons.utils.compression.FileEntryFilter;
public class ZipUtils {
public static List<String> list(byte[] zipData) {
return list(new ByteArrayInputStream(zipData), null);
}
public static List<String> list(InputStream zipData) {
return list(zipData, null);
}
public static List<String> list(InputStream zipData, FileEntryFilter filter) {
return list(zipData, filter, Charset.forName("UTF-8")); //IBM437
}
public static List<String> list(InputStream zipData, FileEntryFilter filter, Charset zipDataFileNameEncoding) {
if (zipData == null) {
return null;
}
ZipInputStream jar = null;
try {
jar = new org.rr.commons.utils.compression.zip.ZipInputStream(zipData, zipDataFileNameEncoding);
final ArrayList<String> result = new ArrayList<>();
ZipEntry nextEntry;
while ((nextEntry = jar.getNextEntry()) != null) {
String name = nextEntry.getName();
if(filter == null || filter.accept(name, nextEntry.getRawName())) {
result.add(name);
}
}
return result;
} catch (IOException e) {
throw new RuntimeException(e);
} finally {
if(jar != null) {
try {jar.close();} catch (IOException e) {}
}
}
}
public static List<CompressedDataEntry> extract(byte[] zipData, Charset zipDataFileNameEncoding, FileEntryFilter filter, int maxEntries) {
return extract(new ByteArrayInputStream(zipData), zipDataFileNameEncoding, filter, maxEntries);
}
public static List<CompressedDataEntry> extract(InputStream zipData, Charset zipDataFileNameEncoding, FileEntryFilter filter, int maxEntries) {
if(zipData == null) {
return null;
}
if(maxEntries < 0) {
maxEntries = Integer.MAX_VALUE;
}
org.rr.commons.utils.compression.zip.ZipInputStream zipIn = null;
try {
zipIn = new org.rr.commons.utils.compression.zip.ZipInputStream(zipData, zipDataFileNameEncoding);
final ArrayList<CompressedDataEntry> result = new ArrayList<>();
final ByteArrayOutputStream bout = new ByteArrayOutputStream();
final byte[] readBuff = new byte[4096];
org.rr.commons.utils.compression.zip.ZipEntry nextEntry;
while ((nextEntry = zipIn.getNextEntry()) != null && maxEntries > result.size()) {
if( filter == null || isFilterAccept(filter, nextEntry) ) {
int len = 0;
while ((len = zipIn.read(readBuff)) != -1) {
bout.write(readBuff, 0, len);
}
if(!nextEntry.isDirectory()) {
result.add(new CompressedDataEntry(nextEntry.getName(), nextEntry.getRawName(), bout.toByteArray()));
}
bout.reset();
}
}
return result;
} catch (IOException e) {
throw new RuntimeException(e);
} finally {
if (zipIn != null) {
try {zipIn.close();} catch (IOException e) {}
}
}
}
private static boolean isFilterAccept(FileEntryFilter filter, org.rr.commons.utils.compression.zip.ZipEntry nextEntry) {
return !nextEntry.isDirectory() && filter.accept(nextEntry.getName(), nextEntry.getRawName());
}
public static List<CompressedDataEntry> extract(InputStream zipData, FileEntryFilter filter, int maxEntries) {
return extract(zipData, Charset.forName("UTF-8"), filter, maxEntries);
}
public static List<CompressedDataEntry> extract(byte[] zipData, FileEntryFilter filter, int maxEntries) {
return extract(zipData, Charset.forName("UTF-8"), filter, maxEntries);
}
public static CompressedDataEntry extract(byte[] zipData, String entry) {
return extract(new ByteArrayInputStream(zipData), entry);
}
public static CompressedDataEntry extract(IResourceHandler zipData, String entry) throws IOException {
ResourceHandlerInputStream contentInputStream = zipData.getContentInputStream();
try {
return extract(contentInputStream, entry);
} finally {
IOUtils.closeQuietly(contentInputStream);
}
}
public static List<CompressedDataEntry> extract(IResourceHandler zipData, FileEntryFilter filter) throws IOException {
ResourceHandlerInputStream contentInputStream = zipData.getContentInputStream();
try {
return extract(contentInputStream, filter, Integer.MAX_VALUE);
} finally {
IOUtils.closeQuietly(contentInputStream);
}
}
/**
* Extracts the entry specified with the entry parameter and returns it.
* @param zipData The zip data containing the file to be extracted.
* @param entry The entry to be extracted. for example 'META-INF/container.xml'
* @return The desired entry or <code>null</code> if the entry is not in the zip.
*/
public static CompressedDataEntry extract(InputStream zipData, final String entry) {
List<CompressedDataEntry> extract = extract(zipData, new FileEntryFilter() {
@Override
public boolean accept(String e, byte[] rawName) {
return entry.equals(e);
}
}, Integer.MAX_VALUE);
if(!extract.isEmpty()) {
return extract.get(0);
}
return null;
}
/**
* Adds or replaces the given entry to existing zip data. Note that the whole
* zip is copied.
* @param zipData The zip data where the given entry should be added.
* @param entry The entry to be added.
* @return the zip data with the added entry or <code>null</code> if something was wrong.
*/
public static byte[] add(byte[] zipData, CompressedDataEntry entry) {
ByteArrayInputStream in = new ByteArrayInputStream(zipData);
ByteArrayOutputStream out = new ByteArrayOutputStream();
add(in, out, entry);
return out.toByteArray();
}
/**
* Adds or replaces the given entry to existing zip data. Note that the whole zip is copied.
* The given InputStream and OutputStream is flushed and closed.
*
* @param zipDateInputStream The zip data where the given entry should be added.
* @param OutputStream zipDateOutputStream The target for the zip data with the new or replaced entry.
* @param entry The entry to be added.
* @return the zip data with the added entry or <code>null</code> if something was wrong.
*/
public static boolean add(InputStream zipDateInputStream, OutputStream zipDateOutputStream, CompressedDataEntry entry) {
return add(zipDateInputStream, zipDateOutputStream, entry, false);
}
/**
* Adds or replaces the given entry to existing zip data. Note that the whole zip is copied.
* The given InputStream and OutputStream is flushed and closed.
*
* @param zipDateInputStream The zip data where the given entry should be added.
* @param OutputStream zipDateOutputStream The target for the zip data with the new or replaced entry.
* @param entry The entry to be added.
* @param storeOnly Did not compress the zip if <code>true</code> and does if <code>false</code>.
* @return the zip data with the added entry or <code>null</code> if something was wrong.
*/
public static boolean add(InputStream zipDateInputStream, OutputStream zipDataOutputStream, CompressedDataEntry entry, boolean storeOnly) {
boolean success = false;
if (entry == null || zipDateInputStream == null || zipDataOutputStream == null) {
return success;
}
ZipOutputStream zipOutputStream = null;
ZipInputStream zipInputStream = null;
try {
zipInputStream = new ZipInputStream(zipDateInputStream);
zipOutputStream = new ZipOutputStream(zipDataOutputStream);
if(storeOnly) {
zipOutputStream.setMethod(ZipEntry.STORED);
} else {
zipOutputStream.setMethod(ZipEntry.DEFLATED);
}
boolean replaceSuccess = false;
ZipEntry zipEntryIn;
while ((zipEntryIn = zipInputStream.getNextEntry()) != null) {
ZipEntry out;
InputStream read;
if(zipEntryIn.getName().equals(entry.path)) {
//replace
out = new ZipEntry(zipEntryIn.getName(), zipEntryIn.getRawName());
read = entry.data;
replaceSuccess = true;
} else {
//add
out = new ZipEntry(zipEntryIn.getName(), zipEntryIn.getRawName());
read = zipInputStream;
}
byte[] readBytes = IOUtils.toByteArray(read);
if(storeOnly) {
//no need with compression
out.setSize(readBytes.length);
long crc = CommonUtils.calculateCrc(readBytes);
out.setCrc(crc);
}
zipOutputStream.putNextEntry(out);
IOUtils.copy(new ByteArrayInputStream(readBytes), zipOutputStream);
zipOutputStream.flush();
zipInputStream.closeEntry();
zipOutputStream.closeEntry();
}
if(!replaceSuccess) {
//new entry must be added
ZipEntry out = new ZipEntry(entry.path, entry.rawPath);
if(storeOnly) {
//no need with compression
byte[] bytes = entry.getBytes();
out.setSize(bytes.length);
long crc = CommonUtils.calculateCrc(bytes);
out.setCrc(crc);
}
InputStream read = entry.data;
zipOutputStream.putNextEntry(out);
IOUtils.copy(read, zipOutputStream);
zipOutputStream.flush();
zipInputStream.closeEntry();
zipOutputStream.closeEntry();
}
success = true;
} catch(Throwable e) {
LoggerFactory.logWarning(ZipUtils.class, "could not add data to zip", e);
success = false;
} finally {
IOUtils.closeQuietly(zipInputStream);
IOUtils.closeQuietly(zipOutputStream);
}
return success;
}
}