Zip File Streaming throws CRC Errors - java

I am trying to read multiple files as BLOB (from database), decompress, zip them and stream the zipped file using JAX-WS. The above code streams the zip file but the downloaded zip file is not opened by windows program throwing CRC errors. After going through some posts, I added zipOutputStream.finish() (just before zipOutStream.flush()) which seemed to fix the error but zips only one file and ignores other files.
Any help is much appreciated
try (ResultSet resultSet = pstmt.executeQuery()) {
try (ByteArrayOutputStream byteStream = new ByteArrayOutputStream()) {
try (ZipOutputStream zipOutputStream = new ZipOutputStream(byteStream)) {
while (resultSet.next()) {
Blob blob = getBlob();
try (InputStream fileInputStream = new GZIPInputStream(blob.getBinaryStream())) {
try (ByteArrayOutputStream fileByteStream = new ByteArrayOutputStream()) {
// Custom method: Copy the above fileInputStream to fileByteStream
IOUtils.copy(fileInputStream, fileByteStream);
ZipEntry entry = new ZipEntry(fileName);
zipOutputStream.putNextEntry(entry);
zipOutputStream.write(fileInputStream.toByteArray());
zipOutputStream.flush();
zipOutputStream.closeEntry();
}
}
try (InputStream inputStream = new ByteArrayInputStream(byteStream.toByteArray());) {
InputStreamDataSource inputDataSource = new InputStreamDataSource(fileName, inputStream);
dataHandler = new DataHandler(inputDataSource);
}
}
}
}
}

Related

zipping files in java memory produces a zip file but the files inside are empty

I am trying to zip multiple files in Java to be used in my jar. 2 files are images and 1 is an HTML temp file. Upon zipping these files, when I try to see the contents of the zip file, all 3 files have become empty. There is no error being thrown as the files are in the zip but they are empty for some reason. I need to save my zip file in memory.
Here is my zip code.
public static File zipPdf(File data, File cover) throws IOException {
ArrayList<ByteArrayOutputStream> zips = new ArrayList<>();
ClassLoader loader = RunningPDF.class.getClassLoader();
File image = new File(Objects.requireNonNull(loader.getResource("chekklogo.png")).getFile());
File man = new File(Objects.requireNonNull(loader.getResource("manlogo.jpg")).getFile());
ByteArrayOutputStream baos = new ByteArrayOutputStream();
try(ZipOutputStream zos = new ZipOutputStream(baos)) {
ZipEntry entry = new ZipEntry(data.getName());
zos.putNextEntry(entry);
ZipEntry entry2 = new ZipEntry(image.getName());
zos.putNextEntry(entry2);
ZipEntry entry3 = new ZipEntry(man.getName());
zos.putNextEntry(entry3);
} catch(IOException ioe) {
ioe.printStackTrace();
}
You forget to write the bytes. The putNextEntry just adds an entry. You need to explicitly write the bytes. Go with the following
File file = new File(filePath);
String zipFileName = file.getName().concat(".zip");
FileOutputStream fos = new FileOutputStream(zipFileName);
ZipOutputStream zos = new ZipOutputStream(fos);
zos.putNextEntry(new ZipEntry(file.getName()));
byte[] bytes = Files.readAllBytes(Paths.get(filePath));
zos.write(bytes, 0, bytes.length);
zos.closeEntry();
zos.close();

Return Zip File from ZipOutputStream in Java

I have a function which creates a Zip file from a list of files. Is it possible to return the Zip file without it being saved on the disk? I need the file as I have to use the zip file as a parameter for another function. I am not sure of the ByteStream would be an option for me.
public File compressFileList(List<File> fileList,String fileName) {
FileOutputStream fileOutputStream=null;
ZipOutputStream zipOutputStream=null;
FileInputStream fileInputStream=null;
String compressedFileName=fileName +".zip";
if(fileList.isEmpty())
return null;
try
{
fileOutputStream = new FileOutputStream(compressedFileName);
zipOutputStream = new ZipOutputStream(new BufferedOutputStream(fileOutputStream));
for (File file: fileList) {
fileInputStream = new FileInputStream(file);
ZipEntry zipEntry = new ZipEntry(file.getName());
zipOutputStream.putNextEntry(zipEntry);
byte[] tmp = new byte[4*1024];
int size = 0;
while((size = fileInputStream.read(tmp)) != -1){
zipOutputStream.write(tmp, 0, size);
}
zipOutputStream.flush();
fileInputStream.close();
}
zipOutputStream.close();
return compressedFile; //This is what I am missing
}
catch (FileNotFoundException e)
{
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
EDIT : adding the use case
The idea is to create a zip file and use the CreateClassifierOptions method of VisualRecognition Service of Watson.
classifierOptions = new CreateClassifierOptions.Builder()
.classifierName("Santa")
.addClass("Santa", new File("C:\\app\\GitRepo\\images\\beagle.zip"))
.negativeExamples(new File("C:\\app\\GitRepo\\images\\nosport.zip"))
.build();
The builder accepts the zip file as the parameter.
Understanding
Based on the explanation from Alexandre Dupriez, I think it is better to store the file at some place on the hard disk.
You should be able to use a ByteArrayOutputStream instead of a FileOutputStream:
zipOutputStream = new ZipOutputStream(new ByteArrayOutputStream());
The difficulty here is to provide a File to the method consuming the zip file. The java.io.File does not provide an abstraction which allows you to manipulate in-memory files.
The java.io.File abstraction and java.io.FileInputStream implementation
To simplify, if we had to boil down what the File abstraction is, we would see it as a URI. And therefore, to be able to build an in-memory File, or at least mimic it, we would need to provide an URI which would then be used by the consumer of the File to read its content.
If we look at the FileInputStream which the consumer is likely to use, we can see that it always ends up with a native call which gives us to possibility whatsoever to abstract a FileSystem for in-memory files:
// class java.io.FileInputStream
/**
* Opens the specified file for reading.
* #param name the name of the file
*/
private native void open0(String name) throws FileNotFoundException;
It would be easier if there was a possibility to adapt the consumer to accept an InputStream, but from your problem statement I guess this is not possible.
API call
Your requirement is to provide a File to the Watson Visual API.
Could you please provide the API method you need to call?
public void compressFileList(List<File> fileList, OutputStream outputStream)
throws IOException {
try (ZipOutputStream zipOutputStream =
new ZipOutputStream(new BufferedOutputStream(outputStream));
for (File file: fileList) {
try (FileInputStream fileInputStream = new FileInputStream(file)) {
ZipEntry zipEntry = new ZipEntry(file.getName());
zipOutputStream.putNextEntry(zipEntry);
byte[] tmp = new byte[4*1024];
int size = 0;
while((size = fileInputStream.read(tmp)) != -1){
zipOutputStream.write(tmp, 0, size);
}
zipOutputStream.flush();
} catch (FileNotFoundException e) { // Maybe skip not found files.
Logger.log(Level.INFO, "File not found {}", file.getPath());
}
}
}
}
Usage:
if (fileList.isEmpty()) {
...
return;
}
try {
compressFileList(fileList, servletRequest.getOutputStream())) {
} catch (FileNotFoundException e) {
...
} catch (IOException e) {
...
}

How to access a zip file into a zip file in Java

I'm trying to read .srt files that are located in zip file itself located in a zip file. I succeed to read .srt files that were in a simple zip with the extract of code below :
for (Enumeration enume = fis.entries(); enume.hasMoreElements();) {
ZipEntry entry = (ZipEntry) enume.nextElement();
fileName = entry.toString().substring(0,entry.toString().length()-4);
try {
InputStream in = fis.getInputStream(entry);
BufferedReader reader = new BufferedReader(new InputStreamReader(in));
String ext = entry.toString().substring(entry.toString().length()-4, entry.toString().length());
But now i don't know how i could get to the zip file inside the zip file.
I tried using ZipFile fis = new ZipFile(filePath) with filePath being the path of the zip file + the name of zip file inside. It didn't recognize the path so i don't know if i am clear.
Thanks.
ZipFile only works with real files, because it's intended for use as a random access mechanism which needs to be able to seek directly to specific locations in the file to read entries by name. But as VGR suggests in the comments, while you can't get random access to the zip-inside-a-zip you can use ZipInputStream, which provides strictly sequential access to the entries and works with any InputStream of zip-format data.
However, ZipInputStream has a slightly odd usage pattern compared to other streams - calling getNextEntry reads the entry metadata and positions the stream to read that entry's data, you read from the ZipInputStream until it reports EOF, then you (optionally) call closeEntry() before moving on to the next entry in the stream.
The critical point is that you must not close() the ZipInputStream until you have finished reading the final entry, so depending what you want to do with the entry data you might need to use something like the commons-io CloseShieldInputStream to guard against the stream getting closed prematurely.
try(ZipInputStream outerZip = new ZipInputStream(fis)) {
ZipEntry outerEntry = null;
while((outerEntry = outerZip.getNextEntry()) != null) {
if(outerEntry.getName().endsWith(".zip")) {
try(ZipInputStream innerZip = new ZipInputStream(
new CloseShieldInputStream(outerZip))) {
ZipEntry innerEntry = null;
while((innerEntry = innerZip.getNextEntry()) != null) {
if(innerEntry.getName().endsWith(".srt")) {
// read the data from the innerZip stream
}
}
}
}
}
}
Find the code to extract .zip files recursively:
public void extractFolder(String zipFile) throws ZipException, IOException {
System.out.println(zipFile);
int BUFFER = 2048;
File file = new File(zipFile);
ZipFile zip = new ZipFile(file);
String newPath = zipFile.substring(0, zipFile.length() - 4);
new File(newPath).mkdir();
Enumeration zipFileEntries = zip.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(newPath, currentEntry);
//destFile = new File(newPath, destFile.getName());
File destinationParent = destFile.getParentFile();
// create the parent directory structure if needed
destinationParent.mkdirs();
if (!entry.isDirectory())
{
BufferedInputStream is = new BufferedInputStream(zip
.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();
}
if (currentEntry.endsWith(".zip"))
{
// found a zip file, try to open
extractFolder(destFile.getAbsolutePath());
}
}
}

How to directly download a file from server,without saving it in any folder?

if(!ErmUtil.isNull(listOfActualFilePaths) && listOfActualFilePaths.size()>0){
FileOutputStream fos = new FileOutputStream("/smiles/wrk/attachments/ermWeb/taxation/testing.zip");
ZipOutputStream zos = new ZipOutputStream(fos);
Iterator itrOnFNames = listOfActualFilePaths.iterator();
while (itrOnFNames.hasNext()) {
StringBuffer ActualPath = (StringBuffer) itrOnFNames.next();
addToZipFile(ActualPath.toString(), zos);
}
zos.close();//Closing Both Streams
fos.close();
}
public void addToZipFile(String fileName, ZipOutputStream zos) throws FileNotFoundException, IOException {
System.out.println("Writing '" + fileName + "' to zip file");
File file = new File(fileName);
int index = fileName.lastIndexOf("/");
String fileNameForZip = fileName.substring(index+1);
FileInputStream fis = new FileInputStream(file);
ZipEntry zipEntry = new ZipEntry(fileNameForZip);
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();
}
I am using above code, to save zip-ed file on a specific location. But what i want to do is that, instead of saving that file, it get downloaded directly.
Edit 1
See If I want to download a zip file,then according to above code, on path /smiles/wrk/attachments/ermWeb/taxation/testing.zip it will be saved first,then from that folder, I(server) can send it to client(Computer).
But I don't want to save it on the specified path,Instead of "saving first to folder and then sending to client", I directly want to send it to client.

Cannot delete zip file after unzipping?

I am facing an unusal issue.I am building a tool which is scheduled to run every 5 mins.
It will pick up the zip files from a particular directory and extract files (depending on the file name) to a destination. I am using zipentry to get each filename in zip file and then extracting as required then I back them (zip files, once I finish all the files in a zip) to a particular directory and then delete the zip file. But sometimes (not always) the zip files do not get deleted. Since I am using fileutils.forcedelete(). I am getting an exception: unable to delete file. So I changed to the code to using fileutils.forcedeleteonexit() still some files remain in the source.
Here is a sample of my code:
sourceFile=new file(zipfile);
zipFile = new ZipFile(sourceFile);
zEnum = (Enumeration<ZipEntry>) zipFile.entries();
for (int a = 0; a < zipFile.size(); a++)
{
ZipEntry zE = zEnum.nextElement();
//Function uses zip4j for extracting. No streams used.
extract(String sourceZipFile, String fileNameToExtract, String outputFolder);
}
//I tried it with finally either
zipFile.close();
//Using fileutils to copy. No streams used.
copyFile(sourceFile, backup);
FileUtils.forceDeleteOnExit(sourceFile);
There are no streams used but I am getting a lock on files sometimes (not always).
What seems to be the bug here? Is it the zip4j extraction that is causing the problem or anything else? I am using zip4j 1.3.1.
I think your problem related with OS file buffers, that sometimes are not flushed when you are trying to delete file.
Did you try to use sourceFile.deleteOnExit() instead FileUtils.forceDeleteOnExit(sourceFile)?
Also you can try to check sourceFile.canWrite before deleting (may be it may helps)
You can also try to use FileInputStream() before deleting:
FileInputStream fi = new FileInputStream(sourceFile);
fi.getFD().sync();
Use apache-commons IO's FileDeleteStrategy. Something like:
FileDeleteStrategy.FORCE.delete(file);
Update:
It should be the way IO is being handled in your application. I have written simple code which copies a zip file to a temporary zip, deflates the temporary zip and after few seconds deletes it. Here you go:
public class ZipTest {
private static String dirPath = "/home/ubuntuuser/Desktop/";
public static void main(String[] args) throws Exception {
File myzip = new File(dirPath + "content.zip");
String tempFileStr = dirPath + "content_temp.zip";
File tempFile = new File(tempFileStr);
String unzipFolderStr = dirPath + "unzip";
copyUsingChannels(myzip, tempFile);
// copyUsingStreams(myzip, tempFile);
unZip(tempFileStr, unzipFolderStr);
Thread.sleep(3000);
tempFile.delete();
}
private static void copyUsingStreams(File myzip, File tempFile)
throws IOException, FileNotFoundException {
byte[] barray = new byte[1024];
if (!tempFile.exists())
{
tempFile.createNewFile();
}
FileOutputStream fos = new FileOutputStream(tempFile);
FileInputStream fis = new FileInputStream(myzip);
int length = 0;
while ((length = fis.read(barray)) != -1)
{
fos.write(barray, 0, length);
}
fis.close();
fos.close();
}
public static void copyUsingChannels(final File srcFile, final File destFile) throws Exception
{
if (!destFile.exists())
{
destFile.createNewFile();
}
FileChannel source = new FileInputStream(srcFile).getChannel();
FileChannel destination = new FileOutputStream(destFile).getChannel();
source.transferTo(0, source.size(), destination);
source.close();
destination.close();
}
private static void unZip(String zipFile, String outputFolder) throws Exception {
byte[] buffer = new byte[1024];
File folder = new File(outputFolder);
if (!folder.exists()) {
folder.mkdir();
}
ZipInputStream zis = new ZipInputStream(new FileInputStream(zipFile));
ZipEntry ze = zis.getNextEntry();
while (ze != null) {
String fileName = ze.getName();
File newFile = new File(outputFolder + File.separator + fileName);
System.out.println("file unzip : " + newFile.getAbsoluteFile());
new File(newFile.getParent()).mkdirs();
if (ze.isDirectory())
{
newFile.mkdir();
ze = zis.getNextEntry();
continue;
}
FileOutputStream fos = new FileOutputStream(newFile);
int len;
while ((len = zis.read(buffer)) > 0) {
fos.write(buffer, 0, len);
}
fos.close();
ze = zis.getNextEntry();
}
zis.closeEntry();
zis.close();
}
}

Categories