I am having a InputStream Object which is actually a zip file. I want to change it back to zip file and save it. I am using DWR's FileTransfer class object to receive the uploaded data from client.
FileTransfer have 3 methods, getInputStream() is one of them. It returns InputStream from FileTransfer object.
In my case, fileTransfer object holds zip file and as well as InputStream object too.
I have done, lot of searches in google. But i am not able to find one example, that illustrates InputStream to zip conversion.
Update
String zipName = file.getName();
String zipType = file.getMimeType();
InputStream zipStream = file.getInputStream();
ZipInputStream zis = new ZipInputStream(zipStream);
System.out.println("File Name: "+zipName+"\n"+"File Type: "+zipType);
int c;
File f2 = new File(DATA_STORE_LOC+dat+".zip");
path.setPath2(DATA_STORE_LOC+dat+".zip");
FileOutputStream fos = new FileOutputStream(f2);
ZipOutputStream zos = new ZipOutputStream(fos);
c = zis.read();
System.out.println(c);
while ((c = zis.read(BUFFER)) != -1) {
zos.write(BUFFER, 0, c);
}
zos.close();
zis.close();
I tried this code, by thought of a typical file copy program. I know it is false, just tried. It gives me java.util.zip.ZipException: ZIP file must have at least one entry.
Any suggestion would be really appreciative!!!!!
See the examples java2s, input and output. If you have more questions feel free to ask them :)
For clarity, in this input example you should do something like:
// FileInputStream fin = new FileInputStream(args[i]);
ZipInputStream zin = new ZipInputStream(ft.getInputStream());
As Don Roby correctly said, if you just want to copy you need not know the file structure and you could use for example static IOUtils.copy(in, out) to copy the file.
Further, if you do wish to extract the ZIP file contents, you should not plainly copy bytes. The ZIP file has a structure, and you extract Entries from the ZIP file, and not just bytes (see the example). Every Entry is a (compressed) file (or the data thereof) with the original name:
ZipEntry ze = null;
while ((ze = zin.getNextEntry()) != null) {
System.out.println("Unzipping " + ze.getName());
FileOutputStream fout = new FileOutputStream(ze.getName());
for (int c = zin.read(); c != -1; c = zin.read()) {
...
Please note the javadoc of getNextEntry():
Reads the next ZIP file entry and positions the stream at the beginning of the entry data.
This positioning is crucial to get to the zipped file contents, and not the metadata.
And I do believe that you accidentally remove the first int:
c = zis.read(); // removing the first
while ((c = zis.read(BUFFER)) != -1) { // so you start with the second?
I believe you mix 2 idioms:
c = zis.read();
while(c != -1) {
...
c = zis.read();
}
and:
int c;
while ((c = zis.read(BUFFER)) != -1) { // so you start with the second?
...
}
I think you can see the difference :)
If your input is a an InputStream from a zip file and your desired output is still a zip file with the same contents, you're just doing a file copy operation and shouldn't have to worry about zip at all. You just need to read from the InputStream and write to a FileOutputStream, more or less as you're doing, but without worrying about wrapping either stream in a zip-aware stream.
ZipInputStream is useful if you have to extract the contents of the zip file as separate files, i.e., to programmatically unzip. And on the other side, ZipOutputStream is used if your have the contents and need to combine them into a zip file.
Related
I converted a zip file of 5 files into a byte array. I want to output a zip file on my disk from that bytearray. My process was first reading the byte[] into a ByteArrayInputStream then into a ZipInputStream.
InputStream plainTextStream = new ByteArrayInputStream(plainText);
ZipInputStream zipInStream = new ZipInputStream(plainTextStream);
I want this to be outputted into a zip file on my disk so here I thought I will need a file and a ZipOutPutStream passing that zip file.
ZipOutputStream zipOutStream = new ZipOutputStream(new FileOutputStream(file));
With a zip entry I traversed the ZipInPutStream writing to a FileOutputStream each entry, using a buffer. At the end of each main loop I put an entry into the ZipOutPutStream.
ZipEntry entry = null;
while((entry = zipInStream.getNextEntry()) != null){
FileOutputStream fileOutStream = new FileOutputStream(entry.getName());
byte[] byteBuff = new byte[1024];
int bytesRead = 0;
while ((bytesRead = zipInStream.read(byteBuff)) != -1)
{
fileOutStream.write(byteBuff, 0, bytesRead);
}
fileOutStream.close();
zipOutStream.putNextEntry(entry);
zipInStream.closeEntry();
}
I add the first file from the zip (there are 5 files), but when trying to add the 2nd file, I get an error on
zipOutStream.putNextEntry(entry)
java.util.zip.ZipException: invalid entry size (expected 18401 but got 0 bytes)
Through debugging I can't figure out where it goes wrong. I assume it may have something to do with the buffer when putting in the first outputstream(entry.getName())? The bytesRead while loop could be an issue.
This is all assuming the logic makes sense. I hope I can approach a solution to this error.
You never write the content of the zipped files to the zip output stream.
You don't need to write the output to a file stream, just write it directly to the zip output stream.
You should be using try-with-resources.
try (ZipInputStream zipInStream = new ZipInputStream(new ByteArrayInputStream(plainText));
ZipOutputStream zipOutStream = new ZipOutputStream(new FileOutputStream(file));
) {
byte[] byteBuff = new byte[1024];
for (ZipEntry entry; (entry = zipInStream.getNextEntry()) != null; ) {
zipOutStream.putNextEntry(entry);
for (int bytesRead; (bytesRead = zipInStream.read(byteBuff)) != -1; ) {
zipOutStream.write(byteBuff, 0, bytesRead);
}
}
}
There is no need to call closeEntry().
To resolve (expected 18401 but got 0 bytes)
Create a new blank excel file.
Copy data from the file you copied from zip to the new created file in step 1.
Use the new file, it should work as its worked for me.
Thanks.
PROBLEM SOLVED IN EDIT 3
I've been struggling with this problem for sometime. All of the questions here in SO or internet seems to work only on 'shallow' structures with one zip inside of another. However I have zip archive which structure is more or less something like this:
input.zip/
--1.zip/
--folder/
----2.zip/
------3.zip/
--------test/
----------some-other-folder/
----------archive.gz/
------------filte-to-parse
----------file-to-parse3.txt
------file-to-parse.txt
--4.zip/
------folder/
and so on so on, my code needs to handle N-level of zips while preserving original zips, gzips, folders and files structure. Using temporary files is forbidden as of lack of privileges (this is something i'm not willing to change).
This is my code I wrote so far, however ZipOutputStream seems to operate only on one (top) level - in case of directories with files/dirs named exactly the same it throws Exception in thread "main" java.util.zip.ZipException: duplicate entry: folder/. It also skips empty directories (which is not expected). What I want to achieve is somehow move my ZipOutputStream to 'lower' level and do operations on each of zips. Maybe there's better approach to handle all of this problem, any help would be appreciated. I need to perform certain text extraction/modification later, however I'm not starting it yet until reading/writing whole structure is not working properly. Thanks in advance for any help!
//constructor
private final File zipFile;
ArchiveResolver(String fileToHandle) {
this.zipFile = new File(Objects.requireNonNull(getClass().getClassLoader().getResource(fileToHandle)).getFile());
}
void resolveInputFile() throws Exception {
FileInputStream fileInputStream = new FileInputStream(this.zipFile);
FileOutputStream fileOutputStream = new FileOutputStream("out.zip");
ZipOutputStream zipOutputStream = new ZipOutputStream(fileOutputStream);
ZipInputStream zipInputStream = new ZipInputStream(fileInputStream);
zip(zipInputStream, zipOutputStream);
zipInputStream.close();
zipOutputStream.close();
}
// this one doesn't preserve internal structure(empty folders), but can work on each file
private void zip(ZipInputStream zipInputStream, ZipOutputStream zipOutputStream) throws IOException {
ZipEntry entry;
while ((entry = zipInputStream.getNextEntry()) != null) {
System.out.println(entry.getName());
byte[] buffer = new byte[1024];
int length;
if (entry.getName().endsWith(".zip")) {
// wrapping outer zip streams to inner streams making actual entries a new source
ZipInputStream innerZipInputStream = new ZipInputStream(zipInputStream);
ZipOutputStream innerZipOutputStream = new ZipOutputStream(zipOutputStream);
ZipEntry zipEntry = new ZipEntry(entry.getName());
// add new zip entry here to outer zipOutputStream: i.e. data.zip
zipOutputStream.putNextEntry(zipEntry);
// now treat this data.zip as parent and call recursively zipFolder on it
zip(innerZipInputStream, innerZipOutputStream);
// Finish internal stream work when innerZipOutput is done
innerZipOutputStream.finish();
// Close entry
zipOutputStream.closeEntry();
} else if (entry.isDirectory()) {
// putting new zip entry into output stream and adding extra '/' to make
// sure zipOutputStream will treat it as folder
ZipEntry zipEntry = new ZipEntry(entry.getName() + "/");
// this only should preserve internal structure
zipOutputStream.putNextEntry(zipEntry);
// reading everything from zipInputStream
while ((length = zipInputStream.read(buffer)) > 0) {
// sending it straight to zipOutputStream
zipOutputStream.write(buffer, 0, length);
}
zipOutputStream.closeEntry();
// This else will include checking if file is respectively:
// .gz file <- then open it, read from file inside, modify and save it
// .txt file <- also read, modify and preserve
} else {
// create new entry on top of this
ZipEntry zipEntry = new ZipEntry(entry.getName());
zipOutputStream.putNextEntry(zipEntry);
while ((length = zipInputStream.read(buffer)) > 0) {
zipOutputStream.write(buffer, 0, length);
}
zipOutputStream.closeEntry();
}
}
}
// This one preserves internal structure (empty folders and so)
// BUT! no work on each file is possible it just preserves everything as it is
private void zipWhole(ZipInputStream zipInputStream, ZipOutputStream zipOutputStream) throws IOException {
ZipEntry entry;
while ((entry = zipInputStream.getNextEntry()) != null) {
System.out.println(entry.getName());
byte[] buffer = new byte[1024];
int length;
zipOutputStream.putNextEntry(new ZipEntry(entry.getName()));
while ((length = zipInputStream.read(buffer)) > 0) {
zipOutputStream.write(buffer, 0, length);
}
zipOutputStream.closeEntry();
}
}
EDIT:
Updated my code to the newest version, still nothing to be proud of but did some changes however still not working... I've added here two very important comments about (in my opinion) code that fails. So I've tested two approaches - the first one is getting ZipInputStream from zipFile by using getInputStream(ZipEntry e); - throws Exception in thread "main" java.util.zip.ZipException: no current ZIP entry when I'm trying to put some entries to ZipOutputStream. The second approach focuses on "wrapping" ZipInputStream into one another -> this results in empty ZipInputStreams with no entries and application just goes through the files, list them (only top level of zips...) and finishes without saving anything into the out.zip file.
EDIT 2:
With a little suggestions from the people in the comments, I've decided to rewrite my code focusing to close, finish and closeEntry in appropriate places (I hope i did it better now). So right now I've achieved a little of something - code iterates through every entry, and saves it into out.zip file with proper zip packaging inside. Still skips empty folders tho, not sure why (I've checked some of the questions on stack and web, seems ok). Anyway thanks for help so far, I'll try to work this out and I'll keep this updated.
EDIT 3:
After few approaches to the problem and some reading + refactoring I've managed to solve this problem (however there's still problem while running this code on Linux - empty directories are skipped, seems to be connected to they way certain OS preserve file information?).
Here's working solution:
void resolveInputFile() throws IOException {
FileInputStream fileInputStream = new FileInputStream(this.zipFile);
FileOutputStream fileOutputStream = new FileOutputStream("in.zip");
ZipOutputStream zipOutputStream = new ZipOutputStream(fileOutputStream);
ZipInputStream zipInputStream = new ZipInputStream(fileInputStream);
zip(zipInputStream, zipOutputStream);
zipInputStream.close();
zipOutputStream.close();
}
private void zip(ZipInputStream zipInputStream, ZipOutputStream zipOutputStream) throws IOException {
ZipEntry entry;
while ((entry = zipInputStream.getNextEntry()) != null) {
logger.info(entry.getName());
if (entry.getName().endsWith(".zip")) {
// If entry is zip, I create inner zip streams that wrap outer ones
ZipInputStream innerZipInputStream = new ZipInputStream(zipInputStream);
ZipOutputStream innerZipOutputStream = new ZipOutputStream(zipOutputStream);
ZipEntry zipEntry = new ZipEntry(entry.getName());
zipOutputStream.putNextEntry(zipEntry);
zip(innerZipInputStream, innerZipOutputStream);
//As mentioned in comments, proper streams needs to be properly closed/finished, I'm done writing to inner stream so I call finish() rather than close() which closes outer stream
innerZipOutputStream.finish();
zipOutputStream.closeEntry();
} else if (entry.getName().endsWith(".gz")) {
GZIPInputStream gzipInputStream = new GZIPInputStream(zipInputStream);
//small trap while using GZIP - to save it properly I needed to put new ZipEntry to outerZipOutputStream BEFORE creating GZIPOutputStream wrapper
ZipEntry zipEntry = new ZipEntry(entry.getName());
zipOutputStream.putNextEntry(zipEntry);
GZIPOutputStream gzipOutputStream = new GZIPOutputStream(zipOutputStream);
//To make it as as much efficient as possible I've used BufferedReader
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(gzipInputStream));
long start = System.nanoTime();
logger.info("Started to process {}", zipEntry.getName());
String line;
while ((line = bufferedReader.readLine()) != null) {
//PROCESSING LINE BY LINE...
zipOutputStream.write((line + "\n").getBytes());
}
logger.info("Processing of {} took {} miliseconds", entry.getName() ,(System.nanoTime() - start) / 1_000_000);
gzipOutputStream.finish();
zipOutputStream.closeEntry();
} else if (entry.getName().endsWith(".txt")) {
ZipEntry zipEntry = new ZipEntry(entry.getName());
zipOutputStream.putNextEntry(zipEntry);
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(zipInputStream));
long start = System.nanoTime();
logger.info("Started to process {}", zipEntry.getName());
String line;
while ((line = bufferedReader.readLine()) != null) {
//PROCESSING LINE BY LINE...
zipOutputStream.write((line + "\n").getBytes());
}
logger.info("Processing of {} took {} miliseconds", entry.getName() ,(System.nanoTime() - start) / 1_000_000);
zipOutputStream.closeEntry();
} else if (entry.isDirectory()) {
//Standard directory preserving
byte[] buffer = new byte[8192];
int length;
// Adding extra "/" to make sure it's dir
ZipEntry zipEntry = new ZipEntry(entry.getName() + "/");
zipOutputStream.putNextEntry(zipEntry);
while ((length = zipInputStream.read(buffer)) > 0) {
// sending it straight to zipOutputStream
zipOutputStream.write(buffer, 0, length);
}
zipOutputStream.closeEntry();
} else {
//In my case it probably will never be called but if there's some different file in here it will be preserved unchanged in the output file
byte[] buffer = new byte[8192];
int length;
ZipEntry zipEntry = new ZipEntry(entry.getName());
zipOutputStream.putNextEntry(zipEntry);
while ((length = zipInputStream.read(buffer)) > 0) {
zipOutputStream.write(buffer, 0, length);
}
zipOutputStream.closeEntry();
}
}
}
Thanks again for all the help and good advices.
There seems to be a lot of debugging and refactoring to be done there.
There's an obvious problem that you are either not closing your streams/entries or doing so in the wrong order. Buffered data will get lost and the central directory not written. (There is a complication that Java streams unhelpfully close the stream they wrap, so there is finish vs close but it still needs to be done in the correct order).
Zip files have no representation for directories as they have a flat structure - the entire file path is included for each entry in both the local header and central directory.
The part of the Java zip library giving a random access interface uses memory mapped files, so you are stuck with streams for everything except, perhaps, the top level.
I have a zip file and I want to replace one file inside it with another file. So do not need to delete a zip entry just replace the file for the zip entry with another.
Here is what I have tried.
public void replaceConfigurationFile(ZipFile zipFile, ZipOutputStream zos, String pathToNewFile, String configFileToReplaced)
throws IOException {
String zipEntryName;
for(Enumeration<?> e = zipFile.entries(); e.hasMoreElements(); ) {
ZipEntry entryIn = (ZipEntry) e.nextElement();
zipEntryName = entryIn.getName();
if(zipEntryName.endsWith(configFileToReplaced)) {
FileInputStream fis = new FileInputStream(pathToNewFile);
ZipEntry zipEntry = new ZipEntry(zipEntryName);
zos.putNextEntry(zipEntry);
byte[] bytes = new byte[1024];
int length;
while ((length = fis.read(bytes)) >= 0) {
zos.write(bytes, 0, length);
}
zos.closeEntry();
fis.close();
} else {
zos.putNextEntry(entryIn);
InputStream is = zipFile.getInputStream(entryIn);
byte [] buf = new byte[1024];
int len;
while((len = (is.read(buf))) > 0) {
zos.write(buf, 0, len);
}
zos.closeEntry();
}
} // enf of for
}
I have a file zip Entry named :
WEB-INF/classes/config/app-dev.yml
and I have a file at location d drive at location
D:/app-dev.yml
I am able to copy the file in to a different zip file by replacing the file i want to replace . But that is really not needed (to create a different file).
So what should I do to just replace the file with my custom file.
I have searched different posts in Stackoverflow, but unable to find what i need. I read that zip entry cannot be deleted but what about replacing it ? Please help
Your problem is that the new file might be larger than the old file - or smaller! You need to do exactly what you did to allow for the change. The probability that the new file, ZIPped, is exactly the same size as the previous one is virtually nil.
The standard Java ZIP file libraries do not allow you to update an existing ZIP file. You need to use a third party library. A Google search should find you a number of alternatives.
But you need to be aware of a couple of things:
If your application ... or system ... crashes while updating the ZIP file, the file may be corrupted.
When a ZIP file is updated, the replacement version of the file will be appended to the ZIP. The old version of the will still be in the ZIP ... but not in the ZIP file index. Hence updating a ZIP file (without rewriting it) wastes disk space.
I have a chararray which holds coordinates in every index. I want to compress the array with zip algorithm and write the zipped version of the array to a file. My idea was that i run the chararray through a zip stream and write it afterwards directly to a file like test.txt. The problem is, that nothing is written to the file after the execution of the code. Can somebody please help me solve that problem?
Kind regards Lorenzo
Here my current code:
byte[] bytes = Charset.forName("UTF-8").encode(CharBuffer.wrap(sample)).array();
FileOutputStream out = new FileOutputStream(cscFile, true);
byte[] compressed = new byte[bytes.length];
ByteArrayInputStream bi = new ByteArrayInputStream(bytes);
ZipInputStream zi = new ZipInputStream(bi);
ZipEntry entry = null;
while ((entry = zi.getNextEntry()) != null) {
zi.read(compressed);
for(int i = 0; i<bytes.length;i++){
out.write(compressed[i]);
}
out.flush();
out.close();
zi.closeEntry();
}
zi.close();
You will probably have to slightly modify your code with something like this:
ZipOutputStream zos = new ZipOutputStream(new FileOutputStream(new File("your zip file name")));
ZipEntry entry = new ZipEntry("zipped file name");
entry.setSize(bytes.length);
zos.putNextEntry(entry);
zos.write(bytes);
zos.closeEntry();
zos.close();
Explanation:
To compress writen data, use ZipOutputStream and give created FileOutputStream as constructor argument. After that, create ZipEntry, which represents file inside your zip file and write contents of byte array into it.
Hope it helps.
See similar question here: https://stackoverflow.com/a/357892/3115098
I am seeking for most efficient way (in terms of speed) to retrieve some file out of the middle of a ZIP file.
e.g. I have ZIP file, which includes 700 folders (tagged 1 to 700). Each folder equals picture and mp3 file. There is special folder called Info, which contains XML file. Problem is, I need to iterate through this ZIP file to find XML file and then I am displaying images from desired folders. I am using ZipFile approach (thus I am iterating through whole ZIP file, even if I want folder 666, I need to go through 665 items in ZIP file) -> selecting from ZIP file is extremely slow.
I would like to ask you, If you have faced similar issue, how have you solved this? Is there any approach in Java, which turns my ZIP file into virtual folder to browse it much more quicker? Is there any external library, which is the most efficient in terms of time?
Source Code snippet:
try {
FileInputStream fin = new FileInputStream(
"sdcard/external_sd/mtp_data/poi_data/data.zip");
ZipInputStream zin = new ZipInputStream(fin);
ZipEntry ze = null;
while ((ze = zin.getNextEntry()) != null) {
// Log.d("ZE", ze.getName());
if (ze.getName().startsWith("body/665/")) {
// Log.d("FILE F", "soubor: "+ze.getName());
if (ze.getName().endsWith(".jpg")
|| ze.getName().endsWith(".JPG")) {
Log.d("OBR", "picture: " + ze.getName());
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
int count;
while ((count = zin.read(buffer)) != -1) {
baos.write(buffer, 0, count);
}
byte[] bytes = baos.toByteArray();
bmp = BitmapFactory.decodeByteArray(bytes, 0,
bytes.length);
photoField.add(bmp);
i++;
}
}
}
}
The ZipFile.getEntry() and ZipFile.getInputStream() methods can be used to access a specific file in a ZIP archive. For example:
ZipFile file = ...
ZipEntry entry = file.getEntry("folder1/picture.jpg");
InputStream in = file.getInputStream(entry);