I have the following code that I use to get the sheet names of an excel file(.xlsx)
XSSFWorkbook workBookXlsx = new XSSFWorkbook(new FileInputStream(pathToFile));
ArrayList<String> sheetNames = new ArrayList<>();
int numberOfSheets = workBookXlsx.getNumberOfSheets();
for (int i = 0; i < numberOfSheets; i++) {
sheetNames.add(workBookXlsx.getSheetAt(i).getSheetName());
}
workBookXlsx = null;
The issue I have with the above code is that it takes a lot of memory(~700MB) & a long time(5-6s) to create the XSSFWorkbook for a file of size 9MB. Even setting the workBookXlsx to null doesn't release the memory taken by the javaw(I know gc may or maynot be called & JVM wont release memory just because I have set a variable to null)
I did go through the documentation of Workbook, XSSFWorkbook & from what I understood, there is no method that will help me get the sheet names with low memory imprint.
The one solution I have found is to manually unzip the .xlsx file and read the contents of the .\xl\woorkbook.xml to get the sheet names and the r:id
Is there an API for getting the sheet names in an .xlsx file without large memory imprint?
To show what #Gagravarr probably meant with his comment:
The XSSFReader contains a method XSSFReader.getSheetsData which "Returns an Iterator which will let you get at all the different Sheets in turn. Each sheet's InputStream is only opened when fetched from the Iterator. It's up to you to close the InputStreams when done with each one.". But as often this is not the whole truth. In truth it returns a XSSFReader.SheetIterator which has a method XSSFReader.SheetIterator.getSheetName to get the sheet names.
Example:
import java.io.InputStream;
import java.io.FileInputStream;
import org.apache.poi.openxml4j.opc.OPCPackage;
import org.apache.poi.xssf.eventusermodel.XSSFReader;
import java.util.Iterator;
public class ExcelXSSFGetSheetNamesXSSFReader {
public static void main(String[] args) throws Exception {
OPCPackage pkg = OPCPackage.open(new FileInputStream("Example.xlsx"));
XSSFReader r = new XSSFReader( pkg );
Iterator<InputStream> sheets = r.getSheetsData();
if (sheets instanceof XSSFReader.SheetIterator) {
XSSFReader.SheetIterator sheetiterator = (XSSFReader.SheetIterator)sheets;
while (sheetiterator.hasNext()) {
InputStream dummy = sheetiterator.next();
System.out.println(sheetiterator.getSheetName());
dummy.close();
}
}
pkg.close();
}
}
Conclusion: Currently you cannot work with apache poi only by trusting the API documentation. Instead you must always have a look at the source code.
Related
I need to create an excel file for upload scenario in jmeter. The excel has 3 columns and number of rows is a dynamic value coming from parameter file.
The row values cannot have same data for different excel. So I am using random string to create data. By hard coding number of rows I am able to create file with below code using apache poi but facing issues to handle dynamic number of rows. Can somebody please provide solution?
Below is the code which is working fine for creating 5 rows.
def path = FileServer.getFileServer().getBaseDir;
def separator = File.separator;
def sourceFileName = "CreateDynamicExcel";
HSSFWorkbook workbook = new HSSFWorkbook();
HSSFSheet sheet = workbook.createSheet("Billing");
Object[] dataTypes = [
["Column1Header","Column2Header","Column3Header"],
["${__RandomString(10,abcdefghij,)}","${__Random(100000000,199999999,)}","${__RandomString(10,abcdefghijklmnopqrst,)}"],
["${__RandomString(10,abcdefghij,)}","${__Random(100000000,199999999,)}","${__RandomString(10,abcdefghijklmnopqrst,)}"],
["${__RandomString(10,abcdefghij,)}","${__Random(100000000,199999999,)}","${__RandomString(10,abcdefghijklmnopqrst,)}"],
["${__RandomString(10,abcdefghij,)}","${__Random(100000000,199999999,)}","${__RandomString(10,abcdefghijklmnopqrst,)}"]];
int rowNum = 0;
for (Object[] datatype:datatypes)
HSSFRow = sheet.createRow(rowNum++);
int colNum = 0;
for(Object filed:datatype){
HSSFCell cell = row.createCell(colNumn+=);
if(filed.instanceof(String){
cell.setCellValue((String) filed);
}
if(filed.instanceof(Integer){
cell.setCellValue((Integer) filed);
}
}
try{
FileOutputStream out = new FileOutputStream(new File(path+separator+sourceFileName+".xls"));
workbook.write(out);
out.close();
}
catch(FileNotFoundException e){
e.printStacktrace();
}
I don't think you should be inlining JMeter Functions or Variables in Groovy scripts because:
It conflicts with Groovy GString Template Engine syntax
Only first occurrence will be cached and used for subsequent iterations
So you can use the following expressions instead:
org.apache.commons.lang3.RandomStringUtils.randomAlphanumeric(10)
org.apache.commons.lang3.RandomUtils.nextInt(100000000, 199999999)
etc.
In case of any problems - take a look at jmeter.log file, in case of any issues you should find the root cause or at least a clue there
I am generating Excel File(.xlsx) using apache poi jar (poi-ooxml-3.9.jar), I added dropdown validation for 10 columns in my excel file, If I generate the Excel File with 50 rows, drop down validation is working. If it exceeds more than 50 rows, drop down validation is not coming in the Excel File, When I open the excel File I get the message as "We found a problem with some content in fileName.xlsx. Do you want us to try to recover as much as we can ? If you trust the source of this workbook, click Yes ". when click on Yes, all the dropdown validation it is removing. Kindly need solution to fix this issue.
Do not create DataValidationConstraint for each single cell but only for each varying list you need. Then create DataValidation using those DataValidationConstraint for continuous CellRangeAddressList which are as big as possible and also are not all single cells.
Example creates ten different list validations for column 1 to 10 in rows 1 to 10000.
import java.io.*;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.*;
import org.apache.poi.hssf.usermodel.*;
import org.apache.poi.ss.util.CellRangeAddressList;
class DataValidationList {
public static void main(String[] args) throws Exception {
Workbook workbook = new XSSFWorkbook(); // or new HSSFWorkbook
Sheet sheet = workbook.createSheet("Data Validation");
DataValidationHelper dvHelper = sheet.getDataValidationHelper();
for (int col = 0; col < 10; col++) {
DataValidationConstraint dvConstraint = dvHelper.createExplicitListConstraint(
new String[]{"Col "+(col+1)+" one","Col "+(col+1)+" two","Col "+(col+1)+" three"});
CellRangeAddressList addressList = new CellRangeAddressList(0, 9999, 0, col);
DataValidation validation = dvHelper.createValidation(
dvConstraint, addressList);
if(validation instanceof XSSFDataValidation) {
validation.setSuppressDropDownArrow(true);
validation.setShowErrorBox(true);
}
else {
validation.setSuppressDropDownArrow(false);
}
sheet.addValidationData(validation);
}
String filename;
if(workbook instanceof XSSFWorkbook) {
filename = "DataValidationList.xlsx";
} else {
filename = "DataValidationList.xls";
}
FileOutputStream out = new FileOutputStream(filename);
workbook.write(out);
out.close();
workbook.close();
}
}
I'm creating a template for users to input data into. All I want them to be able to do is copy their data from their source and put it into Cells A21-D21. Once pasted they cannot delete or alter anything, excel is used only to be able to print. Repasting is fine as this will be a template. Ideally, users would export directly into this protected worksheet and be done with it, but instrument software just calls for excel not a specific database location.
In short, users get data from an instrument and it is saved in a format that it cannot be manipulated but neither can it be shown on any computer except what's connected to the instrument. I need this data to be put into excel but cannot be altered. Auditors can compare the Raw data to the excel if they choose.
Is there a way to have worksheet protected and select the one unlocked cell(Format Cell), A1, and then have the entire range of A21-D21 filled/pasted into?
The thinking is that people will manipulate the raw data to get the answers they want but this will limit users to paste only.
So I guess, simply, I'm hoping to find a way to allow users to copy/paste and THAT'S IT! ?
I don't know if there is a way to do what you want inside Excel, but it can be done programatically.
// Note: need to add reference to Microsoft.Office.Interop.Excel to get these namespaces
// In Visual Studio, choose Project > Add Reference > COM > Type Libraries >
// Microsoft Excel 16.0 Object Library
using Microsoft.Office.Interop.Excel;
using System.Collections.Generic;
using System.Runtime.InteropServices;
namespace ConsoleApp1
{
class Program
{
static void Main(string[] args)
{
string fileName = #"c:\users\eric.sundquist\desktop\book1.xlsx";
int worksheetNumber = 1;
List<string> contents = new List<string> { "1", "2", "3", "4" };
PasteIntoProtectedSheet(fileName, worksheetNumber, contents);
}
static void PasteIntoProtectedSheet(string fileName, int worksheetNumber,
List<string> contents)
{
Application excel = new Application();
Workbook workbook = excel.Workbooks.Open(fileName);
workbook.Sheets[worksheetNumber].Unprotect();
// Can pass in password as parameter if needed
Range range = workbook.Sheets[1].Range("A21:D21");
for (int column = 0; column < contents.Count; column++)
{
range.Cells[1, column + 1] = contents[column];
}
workbook.Sheets[worksheetNumber].Protect();
workbook.Save();
workbook.Close();
Marshal.ReleaseComObject(range);
Marshal.ReleaseComObject(workbook);
Marshal.ReleaseComObject(excel);
}
}
}
I would like to create a progress bar within a Excel-sheet cell. I must use Apache Poi library, but I do not know how to even start. (Something like this, but using the Java library) http://www.tech-recipes.com/rx/35064/excel-2013-create-progress-bars/
I guess I must put a conditional formating, but I do know how it works and I can not find a solution anywhere ... somebody can help me out?
Thanks in advance.
As you suggested, I've used your link to create an example xlsx and simply recreated the necessary xml structures, i.e. open the xlsx file as zip archive and have a look at xl/worksheets/sheet1.xml. Beside the poi-ooxml.jar you'll need the ooxml-schemas-1.1.jar.
(tested with Libre Office 4.0, Excel Viewer 2010, POI 3.10-beta1)
import java.io.FileOutputStream;
import java.lang.reflect.*;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.xssf.usermodel.*;
import org.openxmlformats.schemas.spreadsheetml.x2006.main.*;
public class Databar {
public static void main(String[] args) throws Exception {
Workbook wb = new XSSFWorkbook();
Sheet sheet = wb.createSheet();
for (int i=0; i<4; i++) {
sheet.createRow(i).createCell(0).setCellValue(new int[]{12,38,93,42}[i]);
}
SheetConditionalFormatting cf = sheet.getSheetConditionalFormatting();
XSSFConditionalFormattingRule xcfrule =
(XSSFConditionalFormattingRule)cf.createConditionalFormattingRule("");
Method m = XSSFConditionalFormattingRule.class.getDeclaredMethod("getCTCfRule");
m.setAccessible(true);
CTCfRule cfRule = (CTCfRule)m.invoke(xcfrule);
cfRule.removeFormula(0); // cleanup
cfRule.setType(STCfType.DATA_BAR);
CTDataBar databar = cfRule.addNewDataBar();
CTCfvo vfoMin = databar.addNewCfvo();
vfoMin.setType(STCfvoType.NUM);
vfoMin.setVal("0");
CTCfvo vfoMax = databar.addNewCfvo();
vfoMax.setType(STCfvoType.NUM);
vfoMax.setVal("100");
CTColor color = databar.addNewColor();
color.setRgb(new byte[]{(byte)0xFF, 0x00, 0x00, (byte)0xFF});
CellRangeAddress cra[] = {new CellRangeAddress(0, 3, 0, 0)};
cf.addConditionalFormatting(cra, xcfrule);
FileOutputStream fos = new FileOutputStream("databar-out.xlsx");
wb.write(fos);
fos.close();
}
}
I've been looking on the web for 30 minutes now and can't find any explanation about that. Here is my problem :
I wrote an application with poi to parse some data from 200 excel files or so and put some of it into a new file. I do some cell evaluation with FormulaEvaluator to know the content of the cells before choosing to keep them or not.
Now, when i test it on a test file with only values in the cells, the program works perfectly but when i use it on my pile of files I get this error :
"could not resolve external workbook name"
Is there any way to ignore external workbook references or set up the environment so that it wont evaluate formula with external references?
Because the ones I need don't contain references...
Thank you
Can you not just catch the error, and skip over that cell?
You're getting the error because you've asked POI to evaluate a the formula in a cell, and that formula refers to a different file. However, you've not told POI where to find the file that's referenced, so it objects.
If you don't care about cells with external references, just catch the exception and move on to the next cell.
If you do care, you'll need to tell POI where to find your files. You do this with the setupEnvironment(String[],Evaluator[]) method - pass it an array of workbook names, and a matching array of evaluators for those workbooks.
In order for POI to be able to evaluate external references, it needs access to the workbooks in question. As these don't necessarily have the same names on your system as in the workbook, you need to give POI a map of external references to open workbooks, through the setupReferencedWorkbooks(java.util.Map<java.lang.String,FormulaEvaluator> workbooks) method.
I have done please see below code that is working fine at my side
public static void writeWithExternalReference(String cellContent, boolean isRowUpdate, boolean isFormula)
{
try
{
File yourFile = new File("E:\\Book1.xlsx");
yourFile.createNewFile();
FileInputStream myxls = null;
myxls = new FileInputStream(yourFile);
XSSFWorkbook workbook = new XSSFWorkbook(myxls);
FormulaEvaluator mainWorkbookEvaluator = workbook.getCreationHelper().createFormulaEvaluator();
XSSFWorkbook workbook1 = new XSSFWorkbook(new File("E:\\elk\\lookup.xlsx"));
// Track the workbook references
Map<String,FormulaEvaluator> workbooks = new HashMap<String, FormulaEvaluator>();
workbooks.put("Book1.xlsx", mainWorkbookEvaluator);
workbooks.put("elk/lookup.xlsx", workbook1.getCreationHelper().createFormulaEvaluator());
workbook2.getCreationHelper().createFormulaEvaluator());
// Attach them
mainWorkbookEvaluator.setupReferencedWorkbooks(workbooks);
XSSFSheet worksheet = workbook.getSheetAt(0);
XSSFRow row = null;
if (isRowUpdate) {
int lastRow = worksheet.getLastRowNum();
row = worksheet.createRow(++lastRow);
}
else {
row = worksheet.getRow(worksheet.getLastRowNum());
}
if (!isFormula) {
Cell cell = row.createCell(row.getLastCellNum()==-1 ? 0 : row.getLastCellNum());
cell.setCellValue(Double.parseDouble(cellContent));
} else {
XSSFCell cell = row.createCell(row.getLastCellNum()==-1 ? 0 : row.getLastCellNum());
System.out.println(cellContent);
cell.setCellFormula(cellContent);
mainWorkbookEvaluator.evaluateInCell(cell);
cell.setCellFormula(cellContent);
// mainWorkbookEvaluator.evaluateInCell(cell);
//System.out.println(cell.getCellFormula() + " = "+cell.getStringCellValue());
}
workbook1.close();
myxls.close();
FileOutputStream output_file =new FileOutputStream(yourFile,false);
//write changes
workbook.write(output_file);
output_file.close();
} catch (Exception e) {
e.printStackTrace();
}
}