以下代码是一个测试程序,它使用 Apache POI 创建一个 xlsx blob 并解压缩 blob(xlsx 为 zip 格式)以检查一些值。该代码基于 Stack Overflow 问题,但我无法解压缩以正常工作。对此的要求是不能触及文件系统,因此我们求助于流。
问题是这条线
int size = (int) entry.getSize();
总是产生-1。但是,请参阅之前的评论
zipInput.closeEntry();
用于进一步进入程序但似乎不正确的变体。
我们不想使用 POI 本身来读取 xlsx 文件,因为我们正在测试 POI 似乎不与 Apache Struts 合作的问题,并且我们希望消除我们使用独立于 Struts 的 POI 的方式是罪魁祸首的情况. 一旦这个测试通过,如果 Struts 报告的问题仍然存在,我们知道要查看 Struts 本身而不是我们使用 POI 的方式。
/**
* Short, Self Contained, Correct Example of
* 1) creating an xlsx blob with test data;
* 2) sending it to a stream;
* 3) unzipping the xlsx;
* 4) checking the worksheet's xml file for
* correct test values.
* Heavily inlined from two other properly factored
* files, one POI wrapper and one JUnit test case
* (although the test is not a "unit test" since it
* only tests third party library integration)
* DEPENDENCIES
* 1) poi-3.8.jar
* 2) poi-ooxml-3.8.jar
* 3) xmlbeans-2.6.0.jar
* 4) dom4j-1.6.1.jar
* 5) poi-ooxml-schemas-3.8.jar
* Other solutions attempted:
* 1) commons-compress: always returned byte array of proper size
* but containing all zeros.
* Note that we do NOT want to use the filesystem. This exercise's
* purpose is to create dynamically generated xlsx files delivered
* via HTTP. The filesystem is never involved.
*/
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
public class CreateWorkbook
{
public static void main(String[] args) throws IOException
{
// create the data to place into workbook
List<List<String>> resultset = new ArrayList<List<String>>();
List<String> row = new ArrayList<String>();
row.add("A1");
row.add("B1");
row.add("C1");
resultset.add(row);
row = new ArrayList<String>();
row.add("A2");
row.add("B2");
row.add("C2");
resultset.add(row);
row = new ArrayList<String>();
row.add("A3");
row.add("B3");
row.add("C3");
resultset.add(row);
// create POI workbook and fill it with resultSet
XSSFWorkbook workbook = new XSSFWorkbook();
Sheet s = workbook.createSheet();
for (int i = 0; i < resultset.size(); i++)
{
List<String> record = resultset.get(i);
Row r = s.createRow(i);
for (int j = 0; j < record.size(); j++)
r.createCell(j).setCellValue(record.get(j));
}
ByteArrayOutputStream out = new ByteArrayOutputStream();
workbook.write(out);
out.flush();
// retrieve workbook's data into a string and
// test if the cell values (a1,b2,c3,etc) appear
ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray());
// xlsx is a zipped archive so we need to unzip
ZipInputStream zipInput = new ZipInputStream(in);
assert zipInput.available() == 1;
ZipEntry entry = zipInput.getNextEntry();
assert entry != null;
while (entry != null)
{
// since this "if" condition passes during execution we
// know that the stream contains a zip archive with
// an entry called "xl/worksheets/sheet1.xml". Therefore,
// ZipEntry retrieval appears to work correctly and the
// archive has at least a partially correct zip format.
if (entry.getName().equals("xl/worksheets/sheet1.xml"))
{
int size = (int) entry.getSize();
// ==============================================
// always fails, reports -1
assert size > 0 : size;
// ==============================================
byte[] bytes = new byte[size];
int readbytes = zipInput.read(bytes, 0, size);
assert readbytes > 0 : readbytes;
// nothing after this line has been tested
StringBuilder builder = new StringBuilder();
for (int i = 0; i < size; i++)
builder.append(Character.toString((char) bytes[i]));
String xml = builder.toString();
assert xml.contains("A1");
assert xml.contains("B2");
assert xml.contains("C3");
break;
}
// Swapping the order of these two lines actually
// causes the program to go further, with size = 678
// and failing at the readbytes assertion rather than
// the size assertion.
// I thought swapping would mess everything up since
// the entry would be closed immediately after obtaining
// it.
zipInput.closeEntry();
entry = zipInput.getNextEntry();
}
zipInput.close();
in.close();
}
}