Writing ZipOutputStream to OutPutStream blocking the connection - java

Hi I am working on an android file explorer application. When a large folder is requested for download I am zipping the folder and writing it to the outputstream using the code below.But any further requests are blocked until the file is downloaded.What could be the problem ?Should I consider moving this to a thread?I have tried many variations already and none seem to work.Any pointers in the right direction would be greatly appreciated.
if(f.isDirectory()&& action.equalsIgnoreCase("download")){
File zip=new File(file);
entity = new EntityTemplate(new ContentProducer() {
public void writeTo(final OutputStream outstream) throws IOException {
action=null;
OutputStreamWriter writer=fileop.createZipFile(file, outstream);
writer.flush();
}
});response.setHeader("Content-Type", "application/zip");
response.setHeader("Content-Disposition","attachment; filename=\"" +zip.getName()+".zip\"");
response.setEntity(entity);
}
createZipFileMethod :
OutputStreamWriter createZipFile(String sourceDir,OutputStream os) {
// TODO Auto-generated method stub
OutputStreamWriter osw=null;
try
{
//wrap object of OutputStream
osw=new OutputStreamWriter(os,"UTF-8");
//create object of ZipOutputStream from OutputStream
ZipOutputStream zout = new ZipOutputStream(os);
//create File object from source directory
File fileSource = new File(sourceDir);
addDirectory(zout, fileSource);
//close the ZipOutputStream
zout.close();
System.out.println("Zip file has been created!");
}
catch(IOException ioe)
{
System.out.println("IOException :" + ioe);
}
return osw;
}
private static void addDirectory(ZipOutputStream zout, File fileSource) {
//get sub-folder/files list
File[] files = fileSource.listFiles();
System.out.println("Adding directory " + fileSource.getName());
for(int i=0; i < files.length; i++)
{
//if the file is directory, call the function recursively
if(files[i].isDirectory())
{
addDirectory(zout, files[i]);
continue;
}
/*
* we are here means, its file and not directory, so
* add it to the zip file
*/
try
{
System.out.println("Adding file " + files[i].getName());
//create byte buffer
byte[] buffer = new byte[1024];
//create object of FileInputStream
FileInputStream fin = new FileInputStream(files[i]);
zout.putNextEntry(new ZipEntry(files[i].getName()));
/*
* After creating entry in the zip file, actually
* write the file.
*/
int length;
while((length = fin.read(buffer)) > 0)
{
zout.write(buffer, 0, length);
}
/*
* After writing the file to ZipOutputStream, use
*
* void closeEntry() method of ZipOutputStream class to
* close the current entry and position the stream to
* write the next entry.
*/
zout.closeEntry();
//close the InputStream
fin.close();
}
catch(IOException ioe)
{
System.out.println("IOException :" + ioe);
}
}
}

Related

Creating a download for zip file containing text/csv from string variables

I currently need to create a zip file for downloading. This should contain two (2) csv files that are to be created from string variables. I'm at a loss on how I should do this. My draft is below.
public #ResponseBody Object getFileV1(HttpServletRequest request, HttpServletResponse response) {
try {
response.setContentType("application/zip");
response.setHeader("Content-Disposition", "attachment; filename=Reassigned Tickets Report " + new Date().toString() + ".zip");
String stringValue1 = "This is a test value for csv1";
String stringValue2 = "This is a test value for csv2";
InputStream is1 = new ByteArrayInputStream(stringValue1.getBytes("UTF-8"));
InputStream is2 = new ByteArrayInputStream(stringValue2.getBytes("UTF-8"));
ZipInputStream zin;
ZipEntry entry;
ZipOutputStream zout= new ZipOutputStream(response.getOutputStream());
zin = new ZipInputStream(is1);
entry = zin.getNextEntry();
zout.putNextEntry(entry);
zin = new ZipInputStream(is2);
entry = zin.getNextEntry();
zout.putNextEntry(entry);
zout.closeEntry();
zin.close();
zout.close();
response.flushBuffer();
return null;
} catch (Exception e) {
e.printStackTrace();
return e;
}
}
Obviously this is not working. Probably because I'm still a novice at this. Please bear with me.
I get a "java.lang.NullPointerException" at the line where "zout.putNextEntry" is called. Would appreciate your advice. Thank you in advance.
I solved my problem after a day of looking around. This works for me. But I'm not sure if this is the most efficient way.
public #ResponseBody Object getFileV1(HttpServletRequest request, HttpServletResponse response) {
try {
response.setContentType("application/zip");
response.setHeader("Content-Disposition", "attachment; filename=Test Report " + new Date().toString() + ".zip");
String stringValue1 = "This is a test value for csv1";
String stringValue2 = "This is a test value for csv2";
PrintWriter writer1 = new PrintWriter(new OutputStreamWriter(new FileOutputStream("stringValue1.csv"), "UTF-8"));
writer1.print(stringValue1);
writer1.close();
PrintWriter writer2 = new PrintWriter(new OutputStreamWriter(new FileOutputStream("stringValue2.csv"), "UTF-8"));
writer2.print(stringValue2);
writer2.close();
File file1 = new File("stringValue1.csv");
File file2 = new File("stringValue2.csv");
filesToZip(response, file1, file2);
file1.delete();
file2.delete();
response.flushBuffer();
return null;
} catch (Exception e) {
e.printStackTrace();
return e;
}
}
This is the method I got from another thread with a few edits.
public static void filesToZip(HttpServletResponse response, File... files) throws IOException {
// Create a buffer for reading the files
byte[] buf = new byte[1024];
// create the ZIP file
ZipOutputStream out = new ZipOutputStream(response.getOutputStream());
// compress the files
for(int i=0; i<files.length; i++) {
FileInputStream in = new FileInputStream(files[i].getName());
// add ZIP entry to output stream
out.putNextEntry(new ZipEntry(files[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();
}
The only thing I don't love is that I had to create temporary files and delete them after processing.

Text is not appended in new file in attempt to make a text file manipulator

Problem and where I'm at: I can't append text into these new files I create with the program. Currently it only copies files but does not append them. See line with comment
"// append file name into the new file ".
Secondly, the final dump file seems to only append the .java file, it's not reading or appending the input files.
The explanation of what I'm trying to do:
The long and short is that I am trying to make a program that will be placed into random folders with .txt files filled with data.
I need the program to
Look within the realm of the folder it is in only
Then take any .txt file and
a) make a copy but append the original file name into the text body (at the top), inside a sub-folder like "< filenamehere.txt" into the body (at the top)
b) then copy body contents of the original.txt
c) take the prepended .txt file and append it to a single dump.txt file
d) repeat this for all local txt files and keep appending to the end of the dump.txt file
So at the end, if I had 7 files, I will have 7 appended copies and 1 giant dump file containing everything of the 7 appended copies. So for example, if I had three text files, each one having only one word in them. So a.txt, b.txt, c.txt
and the three words are "Hello world !". The a.txt copy would have the contents inside
">a.txt
Hello
"
Right now it's just copying the Hello (original body content) but not appending the >a.txt. The final dump text file is not accumulating anything from the other files, but it's strangely enough picking up the source code from the .java file. So essentially, I get a //Output folder and inside are the copies of the .txt files and a megaDump.txt that manages to pick up the .java text, but no other text files are appended.
import java.io.* ;
import java.nio.file.*;
public class FilePrepender // class name
{
public static void main (String [] args)
{
// make a giant dump file which we will append all read files into
try {
new File("Output\\").mkdirs();
File megaDumpFile = new File("Output\\masterDump.txt");
if (megaDumpFile.createNewFile()) {
System.out.println("File creation success");
} else {
System.out.println("File was not made. File already exists. Please delete");
}
} catch (IOException e) {
}
//grab file names
File folder = new File(".");
File[] listOfFiles = folder.listFiles();
for (int i = 0; i < listOfFiles.length; i++) {
if (listOfFiles[i].isFile()) {
listOfFiles[i].getName();
} else if (listOfFiles[i].isDirectory()) {
//do nothing
}
}
//open files + duplicate + prepend + and append product to end of master dump file
// main for
for (int j = 0; j < listOfFiles.length; j++){
//append file name for mega dump file
String fileNameTemp = listOfFiles[j].getName(); // get file name
try {
PrintWriter out = new PrintWriter(new BufferedWriter(new FileWriter("Output//masterDump.txt", true)));
out.println(fileNameTemp);
out.flush();
out.close();
}
catch (IOException e) {
}
// duplicate input files
FileInputStream instream = null;
FileOutputStream outstream = null;
try {
File infile =new File(""+listOfFiles[j].getName());
File outfile =new File("Output\\"+listOfFiles[j].getName());
instream = new FileInputStream(infile);
outstream = new FileOutputStream(outfile);
byte[] buffer = new byte[1024];
int length;
// apend file name into the new file
// String fileNameTemp = listOfFiles[j].getName(); // get file name
try {
PrintWriter out = new PrintWriter(new BufferedWriter(new FileWriter("Output//masterDump.txt", true)));
out.println(">"+fileNameTemp);
out.flush();
out.close();
}
catch (IOException e) {
}
// now copy contents of previous file into the new file
/*copying the contents from input stream to
* output stream using read and write methods
*/
while ((length = instream.read(buffer)) > 0){
outstream.write(buffer, 0, length);
}
//Closing the input/output file streams
instream.close();
outstream.close();
// file is copied
} catch(IOException ioe) {
}
// copy newly copied file into mega dump
try {
File infile =new File("Output\\"+listOfFiles[j]); // newly copied
File outfile =new File("Output\\masterDump.txt");
instream = new FileInputStream(infile);
outstream = new FileOutputStream(outfile);
byte[] buffer = new byte[1024];
int length;
/*copying the contents from input stream to
* output stream using read and write methods
*/
while ((length = instream.read(buffer)) > 0){
outstream.write(buffer, 0, length);
}
//Closing the input/output file streams
instream.close();
outstream.close();
// file is copied
} catch(IOException ioe) {
}
} // end for loop
} // end main
} // end class
There are quite a few issues here:
You use for your file paths sometimes slash, sometimes 2 backslashes and sometimes even double slashes which resulted in problems at least on my Mac. Just use regular forward slashes.
The code did not filter for .txt files yet, so everything which was in the current directory was processed - even the executing program itself.
Currently the code wrote the > sometext.txt lines directly into your masterDump.txt instead of indirectly through your file copies.
The code overwrote masterDump.txt for each iteration of the loop as the file was not opened in appending mode.
Following is the code which currently produces the following result when called in a folder with a.txt, b.txt and c.txt containing "Hello", "World" and "!" respectively. I hope this is the desired behavior.
Note that there is much to improve in this code, especially handling the errors as already pointed out in the comments.
import java.io.* ;
import java.nio.file.*;
public class FilePrepender // class name
{
public static void main (String [] args)
{
// make a giant dump file which we will append all read files into
try {
new File("Output/").mkdirs();
File megaDumpFile = new File("Output/masterDump.txt");
if (megaDumpFile.createNewFile()) {
System.out.println("File creation success");
} else {
System.out.println("File was not made. File already exists. Please delete");
}
} catch (IOException e) {
}
//grab file names
File folder = new File(".");
File[] listOfFiles = folder.listFiles();
for (int i = 0; i < listOfFiles.length; i++) {
if (listOfFiles[i].isFile()) {
listOfFiles[i].getName();
} else if (listOfFiles[i].isDirectory()) {
//do nothing
}
}
//open files + duplicate + prepend + and append product to end of master dump file
// main for
for (int j = 0; j < listOfFiles.length; j++){
//append file name for mega dump file
String fileNameTemp = listOfFiles[j].getName(); // get file name
if (!fileNameTemp.toLowerCase().endsWith(".txt")) {
continue;
}
// duplicate input files
FileInputStream instream = null;
FileOutputStream outstream = null;
try {
File infile =new File(""+listOfFiles[j].getName());
File outfile =new File("Output/"+listOfFiles[j].getName());
instream = new FileInputStream(infile);
byte[] buffer = new byte[1024];
int length;
// apend file name into the new file
// String fileNameTemp = listOfFiles[j].getName(); // get file name
outstream = new FileOutputStream(outfile);
PrintWriter out = new PrintWriter(outstream);
out.println(">"+fileNameTemp);
out.flush();
out.close();
// now copy contents of previous file into the new file
/*copying the contents from input stream to
* output stream using read and write methods
*/
outstream = new FileOutputStream(outfile, true);
while ((length = instream.read(buffer)) > 0){
outstream.write(buffer, 0, length);
}
//Closing the input/output file streams
instream.close();
outstream.close();
// file is copied
} catch(IOException ioe) {
}
// copy newly copied file into mega dump
try {
File infile =new File("Output/"+listOfFiles[j]); // newly copied
File outfile =new File("Output/masterDump.txt");
instream = new FileInputStream(infile);
outstream = new FileOutputStream(outfile, true);
byte[] buffer = new byte[1024];
int length;
/*copying the contents from input stream to
* output stream using read and write methods
*/
while ((length = instream.read(buffer)) > 0){
outstream.write(buffer, 0, length);
}
//Closing the input/output file streams
instream.close();
outstream.close();
// file is copied
} catch(IOException ioe) {
}
} // end for loop
} // end main
} // end class
Agreeing with others: You delete your progress every time you 'touch' your masterDump file.
Here is my version:
import java.io.* ;
import java.nio.file.*;
public class FilePrepender // class name
{
public static void main (String [] args)
{
//Generates the string for the output for the right PC.
String OUTFILE="Output"+File.separator+"masterDump.txt";
// make a giant dump file which we will append all read files into
try {
new File("Output").mkdirs();
File megaDumpFile = new File(OUTFILE);
megaDumpFile.createNewFile();
} catch (IOException e) {
System.out.println("Something weird occured!");
}
File folder = new File(".");
// FileFilter filter = new istext();
// File[] listOfFiles = folder.listFiles( filter );
//grab file names
File[] listOfFiles = folder.listFiles();
try {
FileOutputStream fout = new FileOutputStream ( new File(OUTFILE));
PrintWriter pout = new PrintWriter( fout );
for (int j = 0; j < listOfFiles.length; j++){
//Hacky fix cause java is hard:
if ( ! listOfFiles[j].getName().endsWith(".txt")) { continue ; }
//append file name for mega dump file
pout.println(listOfFiles[j].getName()); // Append File-name
pout.flush(); //Probably a better way than this, but eh.
//Copy the file's text
Files.copy(listOfFiles[j].toPath(), fout);
fout.flush(); //Again, eh.
}
pout.close();
pout.close();
}
catch (IOException e) {
}
}
}
/* Ugh, IDK how to java. (This is the non-hacky way, but idk how to.)
public class istext implements FileFilter {
public static void accept(File pathname){
return( pathname.getName().endsWith(".txt"));
}
}
*/

Output zipped directory to ByteArrayOutputStream

I have a directory which I zip with this method:
public byte[] archiveDir(File dir) {
try(ByteArrayOutputStream bos = new ByteArrayOutputStream(); ZipOutputStream zout = new ZipOutputStream(bos)) {
zipSubDirectory("", dir, zout);
return bos.toByteArray();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
private void zipSubDirectory(String basePath, File dir, ZipOutputStream zout) throws IOException {
byte[] buffer = new byte[4096];
File[] files = dir.listFiles();
for (File file : files) {
if (file.isDirectory()) {
String path = basePath + file.getName() + "/";
zout.putNextEntry(new ZipEntry(path));
zipSubDirectory(path, file, zout);
zout.closeEntry();
} else {
FileInputStream fin = new FileInputStream(file);
zout.putNextEntry(new ZipEntry(basePath + file.getName()));
int length;
while ((length = fin.read(buffer)) > 0) {
zout.write(buffer, 0, length);
}
zout.closeEntry();
fin.close();
}
}
}
I then write the bytes to servlet's output stream. But when I receive the zip file, it cannot be opened "the file has wrong format". If I output zipped contents to FileOutputStream and then send file contents to servlet's output stream it works fine. Well, this would solve my problem but in this case I would always have to delete the temporary zip file after its contents are sent to servlet's outpu stream. Is it possible to do this just in memory.
Hmm,
zipSubDirectory(path, file, zout);
zout.closeEntry();
should be:
zout.closeEntry();
zipSubDirectory(path, file, zout);
The main error seems to be that zout is not closed / flushed before toByteArray is called. Here try-with-resources was a bit devious.
try (ByteArrayOutputStream bos = new ByteArrayOutputStream()) {
try ((ZipOutputStream zout = new ZipOutputStream(bos)) {
zipSubDirectory("", dir, zout);
}
return bos.toByteArray();
} catch (IOException e) {
throw new RuntimeException(e);
}

Regarding moving the serialized file based on a condition through java

I have to make a program to copy the serialized files from a source folder to target folder only if the target folder does not contain that
serialized file, so the first condition is to check whether the file that i am copying is already existed in target folder or not
if it exists then do not need to copy but if does not exists then copy, so this check of whether file exists or not is need to be done
at every second
source folder is C:\ter\
target folder is C:\bvg\
file to be transffered is gfr.ser
I have come up with this below program but still check is not implemented please advise how can I implement this check also..
class ScheduledTask extends TimerTask {
public void run() {
InputStream inStream = null;
OutputStream outStream = null;
try {
File source = new File("C:\\ter\\");
File target = new File("C:\\avd\\bvg\\");
// Already exists. do not copy
if (target.exists()) {
return;
}
File[] files = source.listFiles();
for (File file : files) {
inStream = new FileInputStream(file);
outStream = new FileOutputStream(target + "/" + file.getName());
byte[] buffer = new byte[1024];
int length;
// copy the file content in bytes
while ((length = inStream.read(buffer)) > 0) {
outStream.write(buffer, 0, length);
}
inStream.close();
outStream.close();
}
System.out.println("File is copied successful!");
} catch (IOException e) {
e.printStackTrace();
}
}
}
the above approach is not working
You can use exists method of java.io.File class like this.
public void run() {
InputStream inStream = null;
OutputStream outStream = null;
try {
File source = new File("C:\\ter\\gfr.ser");
File target = new File(" C:\\bvg\\gfr.ser");
if (target.exists()){ // Already exists. do not copy
return;
}
inStream = new FileInputStream(source);
outStream = new FileOutputStream(target);
byte[] buffer = new byte[1024];
int length;
// copy the file content in bytes
while ((length = inStream.read(buffer)) > 0) {
outStream.write(buffer, 0, length);
}
inStream.close();
outStream.close();
System.out.println("File is copied successful!");
} catch (IOException e) {
e.printStackTrace();
}
}

Overwriting ZipEntrys

Simple question,
I'm writing a series of text files into a zip, just wrapping a fileoutputstream in a zipoutputstream and then in a printwriter.
public static int saveData(File outfile, DataStructure input) {
//variables
ArrayList<String> out = null;
FileOutputStream fileout = null;
ZipOutputStream zipout = null;
PrintWriter printer = null;
//parameter tests
try {
fileout = new FileOutputStream(outfile);
zipout = new ZipOutputStream(fileout);
printer = new PrintWriter(zipout);
} catch (Exception e) {
e.printStackTrace();
return util.FILE_INVALID;
}
for(DataItem data : input){
//process the data into a list of strings
try {
zipout.putNextEntry(new ZipEntry( dataFileName ));
for(String s : out) {
printer.println(s);
}
zipout.closeEntry();
} catch (Exception e) {
try {
fileout.close();
} catch (Exception x) {
x.printStackTrace();
return util.CRITICAL_ERROR;
}
e.printStackTrace();
return util.CRITICAL_ERROR;
}
}
try {
fileout.close();
} catch (Exception e) {
e.printStackTrace();
return util.CRITICAL_ERROR;
}
return util.SUCCESS;
}
Previously in the app i've been developing I've just been saving to the current directory for testing and I know in the case of a file already existing that the file will be overwritten (and have been exploiting this). What I dont know is the behaviour for zips. Will it overwrite entries of the same name? Or will it simply overwrite the whole zip file (which would be convenient for my purposes.
K.Barad
As Joel said, If you try to add a duplicate ZipEntry you will get an exception. If you want to replace the current entry you need to delete it and re-insert it.
You might want to do something like here below to achieve it:
private ZipFile addFileToExistingZip(File zipFile, File versionFile) throws IOException{
// get a temp file
File tempFile = File.createTempFile(zipFile.getName(), null);
// delete it, otherwise you cannot rename your existing zip to it.
tempFile.delete();
boolean renameOk=zipFile.renameTo(tempFile);
if (!renameOk)
{
throw new RuntimeException("could not rename the file "+zipFile.getAbsolutePath()+" to "+tempFile.getAbsolutePath());
}
byte[] buf = new byte[4096 * 1024];
ZipInputStream zin = new ZipInputStream(new FileInputStream(tempFile));
ZipOutputStream out = new ZipOutputStream(new FileOutputStream(zipFile));
ZipEntry entry = zin.getNextEntry();
while (entry != null) {
String name = entry.getName();
boolean toBeDeleted = false;
if (versionFile.getName().indexOf(name) != -1) {
toBeDeleted = true;
}
if(!toBeDeleted){
// Add ZIP entry to output stream.
out.putNextEntry(new ZipEntry(name));
// Transfer bytes from the ZIP file to the output file
int len;
while ((len = zin.read(buf)) > 0) {
out.write(buf, 0, len);
}
}
entry = zin.getNextEntry();
}
// Close the streams
zin.close();
// Compress the files
InputStream in = new FileInputStream(versionFile);
String fName = versionFile.getName();
// Add ZIP entry to output stream.
out.putNextEntry(new ZipEntry(fName));
// 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();
tempFile.delete();
return new ZipFile(zipFile);
}
The above code worked for me where the need was to add a new zip entry to an existing zip file. If the entry is already present inside the zip, then overwrite it.
Comments/improvements in the code are welcome!
Thanks!
If you try to add a duplicate ZipEntry you will get an exception. If you want to replace the current entry you need to delete it and re-insert it. I suspect the exception you get is much the same as this one.

Categories