I'm uploading zip of excel files as multipart file, but when I create Workbook object of first file, the stream gets closed and I'm not able to read next files.
its working with single file in zip but not with multiple files.
can anybody help? TIA.
try {
ZipInputStream zis = new ZipInputStream(multipartFile.getInputStream());
ZipEntry zipEntry;
while((zipEntry = zis.getNextEntry()) != null) {
XSSFWorkbook workbook = new XSSFWorkbook(zis);
readWorkbook(workbook);
}
zis.close();
} catch (Exception e) {
LOG.error(e);
}
only option is to wrap it so you can intercept close() and prevent it from closing the zip. Something like:
var wrapper = new FilterInputStream(zis) {
#Override public void close() {}
}
Then pass wrapper and not zis to new XSSFWorkbook.
NB: WARNING - your code is severely hampered, essentially buggy. You need to ensure you close that ZipInputStream at the very end, and you're not doing that now. Your exception handling is really bad. You must end all exception blocks with a hard exit, just logging it isn't good enough. throw new RuntimeException("uncaught", e); is the fallback option (many IDEs ship with e.printStackTrace() as default. This is a known extremely stupid default, update your IDE to fix this. Use try-with-resources as well to ensure closing always happens (just calling close() isn't good enough; that close() call you do have in your snippet won't be invoked if exceptions occur.
When reading from a Zipfile, you have an InputStream for the archive. Then you traverse the different entries in there and for each of them you again have an InputStream to read from. Make sure you do not close the entries' InputStreams as that event will close the archive stream.
In your case it may be that the constructor for XSSFWorkbook or the readWorkbook method is closing the stream.
Related
I have this code that copies an array elements into a text file, and after copying the files I have a button which opens the file i-copied.
try
{
print = new PrintWriter("C:\\Users\\Jofrank\\workspace\\Java\\src\\payroll\\report.txt");
print.println("EMPLOYEES PAYROLL RECORD AS OF "+dateFormat.format(date));
print.println();
for(int x=0;x<department.length;x++)
{
print.println("DEPARTMENT: "+department[x].toUpperCase());
print.println("\tPAYROLL PERIOD\tEMPLOYEE NUMBER\tNAME\tPAY RATE\tHOURS WORKED\tSALARY");
print.println();
for(int y=0;y<trans.length;y++)
{
if(trans[y] == null)
{
continue;
}
if(trans[y].getDepartment().equals(department[x]))
{
print.println("\t"+trans[y].getPayrollPeriod()+"\t"+trans[y].getEmpNo()+"\t\t"+trans[y].getName()+"\t"+trans[y].getPayRate()+"\t\t"+trans[y].getHoursWorked()+"\t\t"+String.format("%,.2f", (trans[y].getPayRate()*trans[y].getHoursWorked())));
total+=(trans[y].getPayRate()*trans[y].getHoursWorked());
}
}
print.println("\t\t\t\t\t\t\t\t\tTOTAL:\t"+String.format("%,.2f", total));
print.println();
total=0;
}
print.close();
}
catch (FileNotFoundException e)
{
e.printStackTrace();
}
Unfortunately, My text file was not UPDATED unless i-close the system.
Is there a way that my text file will be updated automatically without CLOSING the system?
You can actually create a PrintWriter with autoFlush turned on:
print = new PrintWriter(new FileOutputStream
("C:\\Users\\Jofrank\\workspace\\Java\\src\\payroll\\report.txt"), true);
Here 2nd parameter is true. As per Javadoc:
autoFlush - A boolean; if true, the println, printf, or format
methods will flush the output buffer
That depens much on the system.
flush() usually should work, but this is all not garuanteed.
There are some embedded flash file system where you might call sync, too.
But for the first apporach try flush()
You need to empty your stream for it to be written to the file. As others have suggested, use .flush() to accomplish this without having to .close() your stream. Otherwise I believe .close() automagically calls .flush() for you to ensure your stream has been emptied and it's contents written to disk or wherever you are directing it.
In documentation it is said that PrintWriter does not flush lines automatically.
You may need to use different constructor for PrintWriter:
PrintWriter(File file)
but you have to open the file itself and then close it after writing is done.
I now have this problem. I want to write a excel file hold in this XSSFWorkbook (workbook) obj into a zip file eg(example.zip while contain this example.xlsx file) to a remote server.
I have tried following but not working, it created a folder with some odd files in the zip file
XSSFWorkbook workbook = new XSSFWorkbook();
//add some data
Zipoutputstream zipstream=new Zipoutputstream(//destination outputstream);
workbook.write(zipstream);
So do anyone knows what's the right way to do this? Thanks in advance
ps workbook.write(fileoutputstream) works but it only write to local disk as a flat file eg test.xlsx instead of inside a zip as I need.
Passing a a ZipOutputStream to XSSFWorkbook.write will result in the stream being hijacked and closed by the workbook. This is because an XSSFWorkbook writes a .xlsx which is itself a zip archive of xml and other files (you can unzip any .xslx to see what's in there).
If you're able to fit the excel file in memory, I've found this to work well:
ZipOutputStream zos = new ZipOutputStream(//destination outputstream);
zos.putNextEntry(new ZipEntry("AnExcelFile.xlsx"));
ByteArrayOutputStream bos = new ByteArrayOutputStream();
workbook.write(bos);
bos.writeTo(zos);
zos.closeEntry();
// Add other entries as needed
zos.close();
Calling close on ByteArrayOutputStream has no effect and can still be written to zos.
You are missing some necessary calls on your ZipOutputStream. You will need to create a ZipEntry for your spreadsheet file, then write it out. You'll need something like
zipstream.putNextEntry(new ZipEntry("example.xlsx"));
Then you should be able to call
workbook.write(zipstream);
But after that you'll need to close the entry before closing the stream.
zipstream.closeEntry();
Please see "Write And Read .Zip File From Java" for details on how to use Java's ZipOutputStream.
Also, be aware that .xlsx files are already compressed zip files, so placing it in a .zip file may not compress it very much.
A colleague of mine, M. Bunshaft, suggested a solution similar to that of Klugscheißer but that does not require the use of a ByteArrayOutputStream, and hence can accommodate larger output.
The idea is to subclass ZipOutputStream, overriding the close() method so it will not do a close.
public class UncloseableZipOutputStream extends ZipOutputStream
{
OutputStream os;
public UncloseableZipOutputStream( OutputStream os )
{
super(os);
}
#Override
/** just flush but do not close */
public void close() throws IOException
{
flush();
}
public void reallyClose() throws IOException
{
super.close();
}
}
Then, simply use it the way you would use the ZipOutputStream.
UncloseableZipOutputStream zos = new UncloseableZipOutputStream(//destination outputstream);
zos.putNextEntry(new ZipEntry("AnExcelFile.xlsx"));
workbook.write(zos);
zos.closeEntry(); // now this will not cause a close of the stream
// Add other entries as needed
zos.reallyClose();
Using the following code, cell value changes made to my Excel Spreadsheet are not saved:
OPCPackage pkg = OPCPackage.open(inputFile);
XSSFWorkbook wb = new XSSFWorkbook(pkg);
ModifyWorkbook();
pkg.close();
The following is a workaround I wrote, but I don't understand why it's necessary.
OPCPackage pkg = OPCPackage.open(inputFile);
XSSFWorkbook wb = new XSSFWorkbook(pkg);
ModifyWorkbook();
File tmp = File.createTempFile("tempSpreadsheet", ".xlsx");
FileOutputStream newExcelFile = new FileOutputStream(tmp);
wb.write(newExcelFile);
newExcelFile.close();
tmp.deleteOnExit();
pkg.close();
The javadocs and guides on the subject indicate that the .close() method should save and close. Checks on the values of the cells modified, prior to closing, indicate that the changes ARE made, but the pkg.close() method alone is not sufficient to write those changes to the file when closed.
A call to OPCPackage.close() will close the underlying OOXML file structure. What it won't do is cause XSSF (or any of the X??F formats) to write their changes out. So, you'll write out an un-changed file....
If you're doing low level modifications, you can open the OPCPackage, make changes, then call close to write them out. If you're doing high level stuff, you open the OPCPackage, work with the UserModel code to make changes, then ask the usermodel to write out its changes. It's this last step that's important.
Your current code is somewhat like:
File f = new File("test.txt");
Sting s = readFileCompletely(f);
s = s.replaceAll("foo", "bar");
f.close();
// Why hasn't the file contents changed?
Reading the file in and modifying the high level objects isn't enough, you also need to tell the high level objects to write out the changes. (The high level objects cache things in memory for performance reasons)
Calling close on an (XSSF)Workbook will call OPCP.close which javadoc states:
Calling myWorkbook.close() should write the changes to file it was
open from.
However, the current version (3.15) seems to have problems doing so. The reason itself is unknown to me, but I discovered that calling myWorkbook.write() will commit not only the changes to the output stream, but also to your current instance after calling close.
A reasonable workaround could therefore be:
try (OutputStream bos = ByteStreams.nullOutputStream()){
workbook.write(bos);
workbook.close();
} catch (IOException e) {
log.error("Could not write Output File", e);
}
Here done with Guavas ByteStreams. Feel free to use other null output stream implementations.
In latest version of POI we dont need to do it manually if we are creating object of org.apache.poi.xssf.usermodel.XSSFWorkbook by default constructor. Read its default constructor method
public XSSFWorkbook() {
super(newPackage());
onWorkbookCreate();
}
protected static OPCPackage newPackage() {
try {
OPCPackage pkg = OPCPackage.create(new ByteArrayOutputStream());
// Main part
PackagePartName corePartName = PackagingURIHelper.createPartName(XSSFRelation.WORKBOOK.getDefaultFileName());
// Create main part relationship
pkg.addRelationship(corePartName, TargetMode.INTERNAL, PackageRelationshipTypes.CORE_DOCUMENT);
// Create main document part
pkg.createPart(corePartName, XSSFRelation.WORKBOOK.getContentType());
pkg.getPackageProperties().setCreatorProperty(DOCUMENT_CREATOR);
return pkg;
} catch (Exception e){
throw new POIXMLException(e);
}
}
I want to create a zip archive in Java where each contained file is produced by serializing some objects. I have a problem with correctly closing the streams.
The code looks like this:
try (OutputStream os = new FileOutputStream(file);
ZipOutputStream zos = new ZipOutputStream(os);) {
ZipEntry ze;
ObjectOutputStream oos;
ze = new ZipEntry("file1");
zos.putNextEntry(ze); // start first file in zip archive
oos = new ObjectOutputStream(zos);
oos.writeObject(obj1a);
oos.writeObject(obj1b);
// I want to close oos here without closing zos
zos.closeEntry(); // end first file in zip archive
ze = new ZipEntry("file2");
zos.putNextEntry(ze); // start second file in zip archive
oos = new ObjectOutputStream(zos);
oos.writeObject(obj2a);
oos.writeObject(obj2b);
// And here again
zos.closeEntry(); // end second file in zip archive
}
I know of course that I should close each stream after finishing using it, so I should close the ObjectOutputStreams in the indicated positions. However, closing the ObjectOutputStreams would also close the ZipOutputStream that I still need.
I do not want to omit the call to ObjectOutputStream.close() because I do not want to rely on the fact that it currently does not more than flush() and reset().
I also cannot use a single ObjectOutputStream instance because then I miss the stream header that is written by the constructor (each single file in the zip archive would not be a full object serialization file, and I could not de-serialize them independently).
The same problem occurs when reading the file again.
The only way I see would be to wrap the ZipOutputStream in some kind of "CloseProtectionOutputStream" that would forward all methods except close() before giving it to the ObjectOutputStream. However, this seems rather hacky and I wonder if I missed a nicer solution in the API.
If your OutputStream wrapper throws an exception when closed more than once, it is not a hack. You can create a wrapper for each zip entry.
From an architectural point of view, I think the ObjectOutputStream author should have provided an option to disable close() cascading. You are just workarounding his lacking API.
In this case, and for all the reasons you mentioned, I would simply not pipe my ObjectOutputStream to the ZipOutputStream. Instead, serialize to a byte[] and then write that straight into the ZipOutputStream. This way, you are free to close the ObjectOutputStream and each byte[] you produce will have the proper header from the serializer. One down side is you wind up with a byte[] in memory that you didn't have before but if you get rid of it right away, assuming we're not talking about millions of objects, the garbage collector shouldn't have a hard time cleaning up.
Just my two cents...
It at least sounds less hacky than a stream subclass that changes the close() behavior.
If you're intending to throw the ObjectOutputStream away anyway, then it should be sufficient to call flush() rather than close(), but as you say in the question the safest approach is probably to use a wrapper around the underlying ZipOutputStream that blocks the close() call. Apache commons-io has CloseShieldOutputStream for this purpose.
I am a bit confused with an error my program started throwing recently.
java.io.IOException: No space left on device
at java.io.FileInputStream.close0(Native Method)
at java.io.FileInputStream.close(FileInputStream.java:259)
at java.io.FilterInputStream.close(FilterInputStream.java:155)
I am assuming that since this is a FileInputStream, that this file is being held in memory, and not on the physical disk. Memory levels look great, and as does disk space. This is especially confusing since it happens on the close of the FileInputStream. Thanks for any explanations you might have as to how this can occur.
EDIT: Code for review
if (this.file.exists()) {
DataInputStream is = new DataInputStream(new FileInputStream(this.file));
this.startDate = new DateTime(is.readLong(), this.timeZone);
this.endDate = new DateTime(is.readLong(), this.timeZone);
is.close();
}
As you can see above I am only opening the file, reading some content, and then closing the file.
In this case, the IOException is thrown from the native method that closes the stream.
The reason it is defined to throw an exception is because the close operation performs a final flush - thus, if an IOException occurs during the flush it will be thrown.
There are several reasons for the exception you have received:
You may lack write permissions on the specific folder.
You may have exceeded your quota.
I also personally suggest that you use the following method of closing the stream:
if (this.file.exists()) {
try {
DataInputStream is = new DataInputStream(new FileInputStream(this.file));
this.startDate = new DateTime(is.readLong(), this.timeZone);
this.endDate = new DateTime(is.readLong(), this.timeZone);
} catch (Exception ex) {
// Handle the exception here
} finally {
is.close();
}
}
You can also use IOUtils method closeQuietly that does not throw an exception 'cause in your case you are not changing the file and you are probably not interested in the result of the close method.
EDIT:
Henry is right. I read InputStream and automatically changed it in my mind to OutputStream.
A close operation on the InputStream does not change the file itself but can change the metadata of the file - such as last access time, etc.