java : corrupted zip file created when copy using nio - java

I have implement following code to copy file(binary file)
code
private void copyFileWithChannels(File aSourceFile, File aTargetFile) {
log("Copying files with channels.");
FileChannel inChannel = null;
FileChannel outChannel = null;
FileInputStream inStream = null;
FileOutputStream outStream = null;
try {
inStream = new FileInputStream(aSourceFile);
inChannel = inStream.getChannel();
outStream = new FileOutputStream(aTargetFile);
outChannel = outStream.getChannel();
long bytesTransferred = 0;
while(bytesTransferred < inChannel.size()){
bytesTransferred += inChannel.transferTo(0, inChannel.size(), outChannel);
}
}
catch(FileNotFoundException e){
log.error("FileNotFoundException in copyFileWithChannels()",e);
}
catch (IOException e) {
log.error("IOException in copyFileWithChannels()",e);
}
catch (Exception e) {
log.error("Exception in copyFileWithChannels()",e);
}
finally {
try{
if (inChannel != null) inChannel.close();
if (outChannel != null) outChannel.close();
if (inStream != null) inStream.close();
if (outStream != null) outStream.close();
}catch(Exception e){
log.error("Exception in copyFileWithChannels() while closing the stream",e);
}
}
}
I have test code with one zip file. when i verify file I found that file which generated is corrupt(size was increased).
Source zip file is about 9GB.

Try this:
while(bytesTransferred < inChannel.size()){
bytesTransferred += inChannel.transferTo(bytesTransferred, inChannel.size() - bytesTransferred, outChannel);
}
Also, I would refer to IOUtils implementation, as a reference
https://github.com/apache/commons-io/blob/master/src/main/java/org/apache/commons/io/FileUtils.java
specifically
private static void doCopyFile(final File srcFile, final File destFile, final boolean preserveFileDate)

The transferTo method's first argument gives the position from which to transfer, not relative to where the stream left off, but relative to the start of the file. Since you put 0 there it will always transfer from the start of the file. So that line needs to be
bytesTransferred += inChannel.transferTo(bytesTransferred , inChannel.size(), outChannel);
mavarazy mentioned in his answer he's not sure if you need a loop when using inChannel.size(), since the expectation is that if you supply the whole size it will copy the entire file. However, the actual transfer might be less than the requested number of bytes if the output channel's buffer has less room available. So you do need the loop as in his second code snippet.

Unless you have a good reason best to use Files.copy(Path, Path, CopyOption...).

Related

Android java zipping files and send with intent

I have this method that zips files from a List and another method that uses this for send it with mail through intent.
My problem is that when I send it two or three times the app crashes and shows me this.
E/StrictMode: A resource was acquired at attached stack trace but never released. See java.io.Closeable for information on avoiding resource leaks.
java.lang.Throwable: Explicit termination method 'close' not called
at dalvik.system.CloseGuard.open(CloseGuard.java:184)
at java.io.FileOutputStream.<init>(FileOutputStream.java:89)
at java.io.FileOutputStream.<init>(FileOutputStream.java:72)
at com.waffles.vatsandbats.VisaDatai.zip(VisaDatai.java:1172)
at com.waffles.vatsandbats.VisaDatai.sendZippedMail(VisaDatai.java:207)
at com.waffles.vatsandbats.VisaDatai.getFiles(VisaDatai.java:298)
at com.waffles.vatsandbats.VisaDatai$7$1.run(VisaDatai.java:1823)
at android.os.Handler.handleCallback(Handler.java:739)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:211)
at android.app.ActivityThread.main(ActivityThread.java:5373)
at java.lang.reflect.Method.invoke(Native Method)
at java.lang.reflect.Method.invoke(Method.java:372)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1020)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:815)
The main problem (I think) is in this message
at com.waffles.vatsandbats.VisaDatai.zip(VisaDatai.java:1172)
That refers to this
in = new FileInputStream(files.get(i)
.getCanonicalFile());
Here's the method that creates the zip and that has the error code
public static File zip(List<File> files, String filename) {
File zipfile = new File(filename);
// Create a buffer for reading the files
FileInputStream in=null;
byte[] buf = new byte[1024];
try {
// create the ZIP file
ZipOutputStream out = new ZipOutputStream(new FileOutputStream(
zipfile));
// compress the files
for (int i = 0; i < files.size(); i++) {
in = new FileInputStream(files.get(i)
.getCanonicalFile());
// add ZIP entry to output stream
out.putNextEntry(new ZipEntry(files.get(i).getName()));
// transfer bytes from the file to the ZIP file
int len;
while ((len = in.read(buf)) > 0) {
out.write(buf, 0, len);
}
// complete the entry
out.closeEntry();
in.close();
}
// complete the ZIP file
out.close();
return zipfile;
} catch (IOException ex) {
ex.printStackTrace();
}
return null;
}
The list of files that I zip is several PrintedPdfDocuments with images and texts (this class has it owns disadvantages but Im lazy changing that right now)
I just can't find the problem. Maybe I need to change the method that zips. Any suggestions?
You should close your streams in a finally block to make sure they are closed properly even when an exception occurs.
Also using getCanonicalFile() creates a new file when you are creating your FileInputStream. You probably want:
in = new FileInputStream(files.get(i));
I solved it. I closed the FileInputStream inside the for loop. So every time it looped and opened the stream I also closed it.
I added a own try catch for the close block at finally just for being sure but when I took away the close section in the for loop it crashed.
Here's the working code
public File zip(List<File> files, String filename) {
File zipfile = new File(filename);
FileInputStream in=null;
ZipOutputStream out=null;
// Create a buffer for reading the files
byte[] buf = new byte[1024];
try {
// create the ZIP file
out = new ZipOutputStream(new FileOutputStream(
zipfile));
// compress the files
for (int i = 0; i < files.size(); i++) {
in = new FileInputStream(files.get(i));
// add ZIP entry to output stream
out.putNextEntry(new ZipEntry(files.get(i).getName()));
// transfer bytes from the file to the ZIP file
int len;
while ((len = in.read(buf)) > 0) {
out.write(buf, 0, len);
}
try {
in.close();
} catch (IOException e1) {
e1.printStackTrace();
}
}
} catch (IOException ex) {
ex.printStackTrace();
}finally {
try {
try {
in.close();
} catch (IOException e1) {
e1.printStackTrace();
}
out.closeEntry();
out.close();
}catch (Exception ex) {
ex.printStackTrace();
}
}
return zipfile;
}

How to Read PNG from a jar packaged in a war

I am trying to write some code that allows me to access a file (specifically EMailBanner.png) that is wrapped as a jar and then included in a war.
The code I have cobbled together is as follows;
public static File getFile(String imagePath){
if(StringUtilities.stringEmptyOrNull(imagePath)){
throw new IllegalArgumentException("Invalid image path");
}
File tempFile = null;
InputStream is = null;
FileOutputStream fos = null;
try{
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
is = classLoader.getResourceAsStream(imagePath);
tempFile = File.createTempFile("EMailBanner", ".png");
tempFile.deleteOnExit();
fos = new FileOutputStream(tempFile);
byte[] buf = new byte[1024];
int len;
while ((len = is.read(buf)) != -1) {
fos.write(buf, 0, len);
}
}catch(IOException e ){
LOGGER.error("Unable to load image", e);
}catch(Exception e){
LOGGER.error("Unable to load image", e);
}finally{
try {
fos.close();
is.close();
} catch (IOException e) {
LOGGER.warn("Unable to close the file input / file output streams", e);
}
}
return tempFile;
}
The issue I am facing is that when deployed on to the development box as a war file - the application cannot find the png file. If I run locally in eclipse it isn't a problem.
Whats strange is I have a number of properties files in the resources folder as you can see from the image below;
I have no problems loading those from within the jar file - loaded like this;
public static Properties getDatabaseConnectionProps(ApplicationName appName) throws IOException{
if(appName == null){
throw new IllegalArgumentException("Path to proeprties file was null or empty");
}
Properties props = null;
try(InputStream resourceStream = DatabaseUtilities.class.getResourceAsStream("/vimba.properties")) {
if(resourceStream != null){
props = new Properties();
props.load(resourceStream);
return props;
}else{
throw new IllegalArgumentException("In invalid properties file path was provided");
}
} catch (IOException e) {
throw e;
}
}
So why would one approach work and potentially not the other? I am completely out of alternative options so really hope someone can save the day
Thanks
I have just tested something similar on my local machine using your code. It seems to work fine from what I can see.
The only other issue I can see is - if you check the JAR (you can de-compile it), make sure the image you are trying to retrieve is in there and that the filename matches.

Does IOUtils.copy block until writing is finished?

Here's my situation: I'm using IOUtils to copy a file. The next thing I do is send a JSON message to another program to say, "You can download the copy". The problem is about 25% of the time the other program gets an error saying "Received unexpected EOF downloading artifact".
Every time this error occurs, if I try again manually, the error doesn't occur. My theory is that IOUtils.copy doesn't block and the OS is still writing the file to the FS while the other program tries to download it. Is there a way to force IOUtils.copy or other functionally equivalent code to block until the OS has finished writing the file? Or is my theory incorrect? Here's the code I'm using:
private boolean archiveArtifact(String archivePath, String deployId, Artifact artifact) {
InputStream inputStream = null;
FileOutputStream fileOutputStream = null;
boolean successful = true;
try {
File archiveDir = new File(archivePath);
File deployDir = new File(archiveDir, deployId);
if (!deployDir.exists()) {
deployDir.mkdirs();
}
URLConnection connection = new URL(artifact.getJenkinsUrl()).openConnection();
inputStream = connection.getInputStream();
File output = new File(deployDir, artifact.getFileName());
fileOutputStream = new FileOutputStream(output);
IOUtils.copy(inputStream, fileOutputStream);
} catch (IOException e) {
successful = false;
logger.error(e.getMessage(), e);
} finally {
try {
if (fileOutputStream != null) {
fileOutputStream.close();
}
} catch (IOException e) {
successful = false;
logger.error(e.getMessage(), e);
}
try {
if (inputStream != null) {
inputStream.close();
}
} catch (IOException e) {
successful = false;
logger.error(e.getMessage(), e);
}
}
return successful;
}
It might be worth noting that I'm copying this to a NFS. Keep in mind I don't really know anything about NFS. This is CentOS release 5.9 (Final).
Your current code only ensures that the file content is passed to the operating system for writing; it does not guarantee that it is actually written to a the disk.
To be certain that the file is actually written to disk you can call sync() on the FileDescriptor:
fileOutputStream.flush();
fileOutputStream.getFD().sync();

Copying files in java

Would you please tell me why does this character " ÿ " appears at the end of my output file. (I use try/catch)
File f1 = new File("C:/Users/NetBeansProjects/QuestionOne/input.txt");
File f2 = new File("C:/Users/NetBeansProjects/QuestionOne/output.txt");
fin = new FileInputStream(f1);
fout = new FileOutputStream(f2);
do {
i = fin.read();
fout.write(i);
} while (i != -1);
The code copies the file content but it ends it with this strange character.
Do I have to include the entire code?
Thanks.
The method fin.read() returns -1 when there's nothing left to read; but you're actually writing that -1 to fout, even though it didn't occur in fin. It shows up as that ÿ character.
One way to write your loop to avoid this problem is
while((i = fin.read()) != -1 ){
fout.write(i);
}
try to use the new Files class introduced in Java 7
public static void copyFile( File from, File to ) throws IOException {
Files.copy( from.toPath(), to.toPath() );
}
Because the last fin.read() will not read anything. According to JavaDoc it will return -1, because of this your fout.write(i) will write that -1. You would do something like this, to correct this behavior:
do {
i = fin.read();
if (i>-1) //note the extra line
fout.write(i);
} while (i != -1);
Or change the do .. while into a while .. do call.
I suggest you should also look at the new NIO API that will perform much better then transferring one character at a time.
File sourceFile = new File("C:/Users/NetBeansProjects/QuestionOne/input.txt");
File destFile = new File("C:/Users/NetBeansProjects/QuestionOne/output.txt");
FileChannel source = null;
FileChannel destination = null;
try {
if (!destFile.exists()) {
destFile.createNewFile();
}
source = new FileInputStream(sourceFile).getChannel();
destination = new FileOutputStream(destFile).getChannel();
destination.transferFrom(source, 0, source.size());
} catch (IOException e) {
System.err.println("Error while trying to transfer content");
//e.printStackTrace();
} finally {
try{
if (source != null)
source.close();
if (destination != null)
destination.close();
}catch(IOException e){
System.err.println("Not able to close the channels");
//e.printStackTrace();
}
}
or you can simply check if(i != -1) before fout
do {
i = fin.read();
if(i != -1)
fout.write(i);
} while (i != -1);

ZipInputStream check whether zip file valid before going to extract it

How we check whether zip file corrupted or valid Zip file before going to extract it
my code`
import java.io.IOException;
import java.io.OutputStream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
public void unzip() {
FileInputStream fin = null;
ZipInputStream zin = null;
OutputStream fout = null;
File outputDir = new File(_location);
File tmp = null;
try {
fin = new FileInputStream(_zipFile);
zin = new ZipInputStream(fin);
ZipEntry ze = null;
while ((ze = zin.getNextEntry()) != null) {
Log.d("Decompress", "Unzipping " + ze.getName());
if (ze.isDirectory()) {
dirChecker(ze.getName());
} else {
tmp = File.createTempFile( "decomp", ".tmp", outputDir );
fout = new BufferedOutputStream(new FileOutputStream(tmp));
DownloadFile.copyStream( zin, fout, _buffer, BUFFER_SIZE );
zin.closeEntry();
fout.close();
fout = null;
tmp.renameTo( new File(_location + ze.getName()) );
tmp = null;
}
}
zin.close();
zin = null;
} catch (IOException e) {
throw new RuntimeException(e);
} finally {
if ( tmp != null ) { try { tmp.delete(); } catch (Exception ignore) {;} }
if ( fout != null ) { try { fout.close(); } catch (Exception ignore) {;} }
if ( zin != null ) { try { zin.closeEntry(); } catch (Exception ignore) {;} }
if ( fin != null ) { try { fin.close(); } catch (Exception ignore) {;} }
}
}
`
this work fine with valid zipfile, but invalid zipfile it doesen't throw any exception not produce anything, but i need to confirm the validity of zip file before going to unzip it
I think it's pretty much useless for checking if the zip file is corrupted for two reasons:
Some zip files contain more bytes than just the zip part. For example, self-extracting archives have an executable part yet they're still valid zip.
The file can be corrupted without changing its size.
So, I suggest calculating the CRC for a guaranteed method of checking for corruption.
A Zip file will be valid as long as it has its zip entry catalog present. If you take the zip command, it will allow you to browse so long as the catalog is present. A parameter used for testing actually performs an extraction and CRC check.
What you can do is extract and do a CRC check on a temporary folder using the temp dir creation facility of Java. Then if it is all successful, commit the extract by copying the files from the temp dir to the final destination.

Categories

Resources