I created an xlsx file using XSSF from apache-poi and creating a route which returns this file using apache-camel.
The file is created ok, I made all the possible content settings that I found, download is working without any problems on my local machine (windows).
After deploying it to a Unix server (under Tomcat7) and accesing the http path from that server, the file is downloaded but it is corrupted, excel will not open it. I receive the following errors:
"Excel found unreadable content in 'Filename.xlsx'. Do you want to recover the contents of this workbook? If you trust the source of this workbook, click Yes." After I click yes follow up error shows:
"Excel cannot open the file 'Filename.xlsx' because the file format or file extension is not valid. Verify that the file has not been corrupted and that the file extension matches the format of the file."
I am using a processor called from camel to create the Workbook file, and create all the content settings in the processor, this should not be a problem, I logged them outside the processor and all is set. Also everything is working as it should, but only on local PC.
Note: I have the same version of Java/apache-camel/poi/tomcat and so on.. like the server.
Some code snippet:
XSSFWorkbook xlsx = writeToExcel(list);
ByteArrayOutputStream bos = getByteFormat(exchange, xlsx);
exchange.getIn().setHeader("Content-Disposition",
"attachment; filename=ProductExports.xlsx");
exchange.getIn().setHeader(Exchange.CONTENT_TYPE,"application/vnd.openxml");
exchange.getIn().setHeader(Exchange.CONTENT_LENGTH,bos.toByteArray().length);
exchange.getIn().setHeader("Expires","0");
exchange.getIn().setBody(bos.toByteArray());
The getByteFormat() part:
private ByteArrayOutputStream getByteFormat(Exchange exchange,
XSSFWorkbook xlsx) throws IOException {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
try {
xlsx.write(bos);
} finally {
bos.close();
}
return bos;
}
I searched around but didn't find any examples with camel-poi, where people encountered the same problems.
Note: I tried also with HSSF format, and with different CONTENT_TYPE settings, the result is the same.
I guess maybe the problem is caused cause I pass the byte array and somehow Unix systems are interpreting this in a different way, or maybe something with file transfer partitioning. The corrupted file has a bigger dimension then the one which is created normally, probably contains some extra lines, not sure if this matters.
Below the code working fine in Windows and Unix. Hope this will may help you.
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
OutputStream outStream = null;
try {
response.setContentType("application/vnd.ms-excel");
response.setHeader
("Content-Disposition", "attachment; filename=Sample.xlsx");
outStream = response.getOutputStream();
Workbook wb = new XSSFWorkbook();
XSSFSheet sheet = wb.createSheet(sheetName) ;
//iterating r number of rows
for (int r=0;r < 5; r++ )
{
XSSFRow row = sheet.createRow(r);
//iterating c number of columns
for (int c=0;c < 5; c++ )
{
XSSFCell cell = row.createCell(c);
cell.setCellValue("Cell "+r+" "+c);
}
}
wb.write(outStream);
outStream.close();
}
catch (Exception e){
throw new ServletException("Exception in Excel Sample Servlet", e);
}
finally{
if (outStream != null)
outStream.close();
}
}
Related
byte[] test = getByteArry(excelfikepath)
I have one method where it returns the bytearray of the excel .xlsx file. To read this file i need to write these byte array using FileOutputStream on one server and from there i am calling another method which will read and process that excel from the server.
There is some limitation because of which i cant read excel file directly i have to put it onto another server and process.
Just wanted to know is there any way by which i can make use of this byte array and read excel file IN MEMORY instead of writing it on server.
This will help to get byte array out of an excel file.
public static byte[] getFileByteArr(String fileName) throws InvalidFormatException, IOException {
try (OPCPackage opcPackage = OPCPackage.open(new File(fileName))) {
try (XSSFWorkbook workbook = (XSSFWorkbook) WorkbookFactory.create(opcPackage)) {
try (ByteArrayOutputStream bos = new ByteArrayOutputStream()) {
workbook.write(bos);
return bos.toByteArray();
}
}
}
}
i have a excel file which serve as a template and will be writted and downloaded by several users
I am using apache poi for doing this
The problem is when one user fill the excel (using a gui client) it will be writted directly to the template but as a template it should stay unmodified otherwise next user will have previous user changes ....
The best solution i think is to write to a temp files which will be deleted at user's action exit
Thank you very much
File excelFile = openTemplate();
OPCPackage pkg = OPCPackage.open(excelFile ,PackageAccess.READ_WRITE);
XSSFWorkbook book = new XSSFWorkbook(pkg);
book.setWorkbookType(XSSFWorkbookType.XLSX);
book.setForceFormulaRecalculation(true);
// excel treatments ( sheet , styles etc..)
FileOutputStream out =
new FileOutputStream(TempFile.createTempFile("Export" , ".xlsx"));
book.write(out);
ByteArrayOutputStream bos = new ByteArrayOutputStream();
bos.writeTo(out);
bytes = bos.toByteArray();
out.close();
bos.close();
book.close();
return bytes;
I finally managed to find a solution which resolves my problem
It seems that whenever i call close method on a workbook , it will automatically save the file on hard disk as coded in the library
public void close() throws IOException {
if (this.packageAccess == PackageAccess.READ) {
logger.log(POILogger.WARN,
"The close() method is intended to SAVE a package. This package is open in READ ONLY mode, use the revert() method instead !");
revert();
return;
}
if (this.contentTypeManager == null) {
logger.log(POILogger.WARN,
"Unable to call close() on a package that hasn't been fully opened yet");
return;
}
// Save the content
ReentrantReadWriteLock l = new ReentrantReadWriteLock();
try {
l.writeLock().lock();
if (this.originalPackagePath != null
&& !"".equals(this.originalPackagePath.trim())) {
File targetFile = new File(this.originalPackagePath);
if (!targetFile.exists()
|| !(this.originalPackagePath
.equalsIgnoreCase(targetFile.getAbsolutePath()))) {
// Case of a package created from scratch
save(targetFile);
} else {
closeImpl();
}
} else if (this.output != null) {
save(this.output);
output.close();
}
} finally {
l.writeLock().unlock();
}
// Clear
this.contentTypeManager.clearAll();
}
See http://apache-poi.1045710.n5.nabble.com/Validating-office-files-without-saving-them-td5722653.html and https://bz.apache.org/bugzilla/show_bug.cgi?id=59287
It happens only when a File or OPCPackage is provided to the workbook.
The solution is to work with InputStream : XSSFWorkbook book = new XSSFWorkbook(new FileInputStream(file)); which will not overwrite the template as i wanted
After opening and immediately closing an xlsx-file, created with Apache POI XSSF, I get prompted to save unsaved changes. As far as i can tell, this is happening because I am using formulas within the xlsx-file.
According to the javadoc, this should be bypassed by setting XSSFWorkbook.setForceFormulaRecalculation(true)
However, this doesn't solve the problem.
I also tried to manually recalculate the formulas before saving the file without success.
SSCCE:
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFFormulaEvaluator;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
public class XSSFExample {
public static void main(String[] args) {
// Create workbook and sheet
Workbook wb = new XSSFWorkbook();
Sheet sheet = wb.createSheet("Sheet 1");
// Create a row and put some cells in it.
Row row = sheet.createRow((short) 0);
row.createCell(0).setCellValue(5.0);
row.createCell(1).setCellValue(5.0);
row.createCell(2).setCellFormula("A1/B1");
// Write the output to a file
try (FileOutputStream fileOut = new FileOutputStream("XSSFExample.xlsx")) {
wb.setForceFormulaRecalculation(false);
System.out.println(wb.getForceFormulaRecalculation()); // prints "false"
XSSFFormulaEvaluator.evaluateAllFormulaCells((XSSFWorkbook) wb); // this doesn't seem to make any difference
wb.write(fileOut);
} catch (IOException ex) {
Logger.getLogger(XSSFExample.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
What can I do to create the file and not get prompted to save it after I opened it the first time?
Update:
As stated here (https://poi.apache.org/spreadsheet/eval.html#recalculation) I also tried another method to manually recalculate with no success. Even re-reading the file after save, recalc and save as a second file doesn't work.
Update 2:
Considering the accepted answer, I was able to solve the problem by adding following lines of code to the above SSCCE:
(Please note that this was just a "quick and dirty" attempt to solve the problem. There are probably a lot of improvements possible).
ZipFile zipFile = new ZipFile("XSSFExample.xlsx");
final ZipOutputStream zos = new ZipOutputStream(new FileOutputStream("XSSFExample_NoSave.xlsx"));
for (Enumeration e = zipFile.entries(); e.hasMoreElements();) {
ZipEntry entryIn = (ZipEntry) e.nextElement();
if (!entryIn.getName().equalsIgnoreCase("xl/workbook.xml")) {
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);
}
} else {
zos.putNextEntry(new ZipEntry("xl/workbook.xml"));
InputStream is = zipFile.getInputStream(entryIn);
byte[] buf = new byte[1024];
int len;
while (is.read(buf) > 0) {
String s = new String(buf);
String searchFileVersion = "/relationships\"><workbookPr";
String replaceFileVersion = "/relationships\"><fileVersion appName=\"xl\" lastEdited=\"5\" lowestEdited=\"5\" rupBuild=\"9303\"/><workbookPr";
String searchCalcId = "<calcPr calcId=\"0\"/>";
String replaceCalcId = "<calcPr calcId=\"" + String.valueOf(Integer.MAX_VALUE) + "\"/>";
if (s.contains(searchFileVersion)) {
s = s.replaceAll(searchFileVersion, replaceFileVersion);
}
if (s.contains(searchCalcId)) {
s = s.replaceAll(searchCalcId, replaceCalcId);
}
len = s.trim().length();
buf = s.getBytes();
zos.write(buf, 0, (len < buf.length) ? len : buf.length);
}
}
zos.closeEntry();
}
zos.close();
Even I was facing the same issue but after adding the below line, the issue has been resolved.
wb.getCreationHelper().createFormulaEvaluator().evaluateAll();
PROBLEM
The problem could lie in MS Excel itself (once you are sure that all formulas were calculated and saved in the .xlsx file). According to my testing, Excel will recalculate all formulas during opening if it finds out that the file was last saved by older version of Excel or other application (the point is that the version numbers doesn't match and/or are lower than current version of Excel opening the file) to maintain good compatibility.
SOLUTION
(making Excel think that the .xlsx file was generated by the same Excel version to avoid recalculation)
Excel reads all file versioning info from workbook.xml file located in xl directory inside .xlsx archive (.xlsx is just a zipped archive).
workbook.xml file generated by Apache POI could look like this:
<workbook xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships">
<workbookPr date1904="false"/>
<bookViews><workbookView activeTab="0"/></bookViews>
<sheets>
<sheet name="new sheet" r:id="rId3" sheetId="1"/>
</sheets>
<calcPr calcId="0"/>
</workbook>
The file generated by Excel 2010 looks like this:
<workbook xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships">
<fileVersion appName="xl" lastEdited="5" lowestEdited="5" rupBuild="9303"/>
<workbookPr defaultThemeVersion="124226"/>
<bookViews><workbookView xWindow="630" yWindow="510" windowWidth="27495" windowHeight="14505"/></bookViews>
<sheets>
<sheet name="new sheet" sheetId="1" r:id="rId1"/>
</sheets>
<calcPr calcId="145621"/>
</workbook>
Notice the <fileVersion> tag completely missing in POI generated file and <calcPr> tag with calcId set to some real value in Excel generated file.
I was able to avoid Excel 2010 automatic formula recalculation (and annoying "Save changes" dialog) by inserting correlated <fileVersion> tag and setting calcId to equal or greater number than the number generated by my current version of Excel to the workbook.xml generated by POI.
More information regarding the workbook.xml format can be found on MSDN Open XML SDK documentation.
I am using Apache POI 5.2.2, open template.xlsx file with just one sheet, clone 1..n new sheets, write cells, delete 1st template sheet, save .xlsx file.
Opening a file in Excel and close gives Save changes? prompt even if did nothing, no #formula cells, no external links or objects in a workbook. I realized if the number of worksheets is different than the original file then prompt is shown.
All sheets had xl/worksheets/sheet1.xml#xr:uid={00000000-0001-0000-0000-000000000000} zero guid.
Text editing sheetX.xml#xr:uid values to {11111111-1111-1111-1111-112233440001}, {11111111-1111-1111-1111-112233440002}, {11111111-1111-1111-1111-112233440003}, .. unique guids fixed a problem.
Using #sobrino's answer this is a modified unzip-zip fix.
public void fixFile(File inputFile, File outputFile) throws IOException {
int count=0;
ZipFile zipFile = new ZipFile(inputFile);
final ZipOutputStream zos = new ZipOutputStream(new FileOutputStream(outputFile));
for (Enumeration<? extends ZipEntry> en = zipFile.entries(); en.hasMoreElements();) {
ZipEntry entryIn = (ZipEntry)en.nextElement();
String name = entryIn.getName();
if(!( name.startsWith("xl/worksheets/") && name.endsWith(".xml")
&& name.indexOf('/', 14)<0 )) {
zos.putNextEntry(entryIn);
InputStream is = zipFile.getInputStream(entryIn);
byte[] buf = new byte[2*1024];
int len;
while ((len = (is.read(buf))) > 0) {
zos.write(buf, 0, len);
}
} else {
// fix xr:uid="{00000000-0001-0000-0000-000000000000}" zero GUID to avoid "save changes" prompt
// <worksheet ... xr:uid="{11111111-1111-1111-1111-112233440001" ...
count++;
zos.putNextEntry(new ZipEntry(name));
InputStream is = zipFile.getInputStream(entryIn);
byte[] buf = new byte[2*1024];
int len;
boolean firstRead=true;
while ( (len=is.read(buf)) > 0) {
if(firstRead) {
firstRead=false;
String sData=new String(buf,0,len, "UTF-8");
int delimS=sData.indexOf("xr:uid=\"");
int delimE=sData.indexOf('"', delimS+8);
int delimG=sData.indexOf("-000000000000}", delimS+8);
if(delimG>0 && delimG<=delimE && delimS>0) {
// found zero GUID, replace value
sData=sData.substring(0, delimS+8)
+ String.format("{11111111-1111-1111-1111-11223344%04x}", count)
+ sData.substring(delimE);
zos.write(sData.getBytes("UTF-8"));
} else {
zos.write(buf, 0, len);
}
} else {
zos.write(buf, 0, len);
}
}
}
zos.closeEntry();
}
zos.close();
zipFile.close();
}
public Sheet readExcel() throws Exception{
//File fi=new File(new File(System.getProperty("user.dir"))+"\\src\\testdata2.xls");
File fi=new File("C:\\Users\\admin\\workspace\\HMS\\src\\testdata\\testdata1.xlsx");
Workbook wb = new XSSFWorkbook(fi);
Sheet Sheet = wb.getSheetAt(0);
int rowCount = Sheet.getLastRowNum()-Sheet.getFirstRowNum();
for (int i = 1; i < rowCount+1; i++) {
Row row = Sheet.getRow(i);
if(row.getCell(0).toString().length()==0){
System.out.println(row.getCell(1).toString()+"----"+ row.getCell(2).toString()+"----"+
row.getCell(3).toString()+"----"+ row.getCell(4).toString());
}
}
return Sheet;
}
By running above code am getting error like this........
Exception in thread "main" java.lang.IllegalStateException: Zip File
is closed
at org.apache.poi.openxml4j.util.ZipFileZipEntrySource.getEntries(ZipFileZipEntrySource.java:45)
at org.apache.poi.openxml4j.opc.ZipPackage.getPartsImpl(ZipPackage.java:186)
at org.apache.poi.openxml4j.opc.OPCPackage.getParts(OPCPackage.java:684)
at org.apache.poi.openxml4j.opc.OPCPackage.open(OPCPackage.java:254)
at org.apache.poi.openxml4j.opc.OPCPackage.open(OPCPackage.java:201)
at org.apache.poi.xssf.usermodel.XSSFWorkbook.(XSSFWorkbook.java:294)
at ExcelReader.readExcel(ExcelReader.java:16)
at ExcelReader.main(ExcelReader.java:30)
Can anyone help me tracing out what exactly is the problem.
I Googled but couldn't get the solution!
To read an xslx file use create an object of FileInputStream class
//Create a object of File class to open xlsx file
File file = new File("path/filename.xlsx");
//Create an object of FileInputStream class to read excel file
FileInputStream inputStream = new FileInputStream(file);
//create object of XSSFWorkbook class
Workbook wb = new XSSFWorkbook(inputStream);
Hope this heps you...
Try shortening the file name and file path. Looks like there is a limit on how long the characters can be between " and ". It worked for me!
You don't necessarily need to pass a FileInputStream when creating a XSSFWorkbook object, you can also pass an absolute path+filename as a String and it can be quite long (works for me with 101 characters, 108 characters with the double slashes, could probably be longer). I just wrote a small local app under Windows 7 whose only argument is a property file containing (among other properties) the absolute path+filename of the .xlsx file that I want to deal with (example property format : datasetFile=C:\\Users\\jlm\\Documents\\Test Cases\\AAAS\\TestCase2JsonGenerator\\AAAS_g1.xlsx). I just pass the datasetFile property as parameter to the XSSFWorkbook constructor (example code line : wb = new XSSFWorkbook(tags.get("datasetFile")); ). It works just fine, BUT don't forget any of the double slashes otherwise you get the "Zip File is closed" exception (about 2 hours lost).
I am trying to zip a group of binary data (result set returned from database) into a single file. Which can be downloaded via web application. Following code is used to zip the result set and write the zip file to HttpServletResponse
String outFilename = "outfile.zip";
response.setContentType("application/octet-stream");
response.setHeader("Content-Disposition", "attachment; filename= " + outFilename);
OutputStream os = response.getOutputStream();
ZipOutputStream out = new ZipOutputStream(os);
for (int i = 0; i < cardFileList.size(); i++) {
CardFile cardFile = cardFileList.get(i);
out.putNextEntry(new ZipEntry(cardFile.getBinaryFileName()));
out.write(cardFile.getBinaryFile(), 0, cardFile.getBinaryFile().length);
out.closeEntry();
}
// Complete the ZIP file
out.flush();
out.close();
os.close();
The problem is that while unzipping the downloaded zip file using WinRar I get following error :
File Path: Either multipart or corrupt ZIP archive
Can someone point out where am I making mistake?. Any help would be appreciated.
[EDIT] I tried response.setContentType("application/zip"); but same result.
The following code works for me:
FileOutputStream os = new FileOutputStream("out.zip");
ZipOutputStream zos = new ZipOutputStream(os);
try
{
for (int i = 1; i <= 5; i++)
{
ZipEntry curEntry = new ZipEntry("file" + i + ".txt");
zos.putNextEntry(curEntry);
zos.write(("Good morning " + i).getBytes("UTF-8"));
}
}
finally
{
zos.close();
}
Zip files generated with this code opens up with 7-zip without problem.
Check that the response from the servlet is not actually a 404 or 500 error page. Pick a small response and open it up with a hex editor or even a text editor. Zip files start with a 'PK' magic number which should be visible even in a text editor.
Try saving to file instead of servlet output stream for starters and see if that makes a difference.
Could there be a filter modifying your servlet's output / corrupting the ZIP?
Your code looks correct for producing the file. So the problem is probably in your downloading.
look at the file you have downloaded. What is it's size?
try a tool other than your WinRar and see what you have. 7Zip is decent.