diff --git a/build.gradle b/build.gradle index f7c02dd0fe4..b03e440ce4c 100644 --- a/build.gradle +++ b/build.gradle @@ -837,6 +837,7 @@ project("spring-webmvc") { optional("javax.servlet.jsp.jstl:javax.servlet.jsp.jstl-api:1.2.1") optional("net.sourceforge.jexcelapi:jxl:2.6.12") optional("org.apache.poi:poi:3.11") + optional("org.apache.poi:poi-ooxml:3.11") optional("org.apache.velocity:velocity:1.7") optional("velocity-tools:velocity-tools-view:1.4") optional("org.freemarker:freemarker:${freemarkerVersion}") diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/view/document/AbstractExcelView.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/view/document/AbstractExcelView.java index c3ff4f5d8cb..c5a1d619afc 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/view/document/AbstractExcelView.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/view/document/AbstractExcelView.java @@ -26,7 +26,6 @@ import org.apache.poi.hssf.usermodel.HSSFCell; import org.apache.poi.hssf.usermodel.HSSFRow; import org.apache.poi.hssf.usermodel.HSSFSheet; import org.apache.poi.hssf.usermodel.HSSFWorkbook; -import org.apache.poi.poifs.filesystem.POIFSFileSystem; import org.springframework.core.io.Resource; import org.springframework.core.io.support.LocalizedResourceHelper; @@ -92,7 +91,10 @@ import org.springframework.web.servlet.view.AbstractView; * @author Jean-Pierre Pawlak * @author Juergen Hoeller * @see AbstractPdfView + * @deprecated as of Spring 4.2, in favor of {@link AbstractXlsView} and its + * {@link AbstractXlsxView} and {@link AbstractXlsxStreamingView} variants */ +@Deprecated public abstract class AbstractExcelView extends AbstractView { /** The content type for an Excel response */ @@ -172,8 +174,7 @@ public abstract class AbstractExcelView extends AbstractView { if (logger.isDebugEnabled()) { logger.debug("Loading Excel workbook from " + inputFile); } - POIFSFileSystem fs = new POIFSFileSystem(inputFile.getInputStream()); - return new HSSFWorkbook(fs); + return new HSSFWorkbook(inputFile.getInputStream()); } /** @@ -194,7 +195,7 @@ public abstract class AbstractExcelView extends AbstractView { *

Creates the row and the cell if they still doesn't already exist. * Thus, the column can be passed as an int, the method making the needed downcasts. * @param sheet a sheet object. The first sheet is usually obtained by workbook.getSheetAt(0) - * @param row thr row number + * @param row the row number * @param col the column number * @return the HSSFCell */ diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/view/document/AbstractXlsView.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/view/document/AbstractXlsView.java new file mode 100644 index 00000000000..7f06cd70ed3 --- /dev/null +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/view/document/AbstractXlsView.java @@ -0,0 +1,121 @@ +/* + * Copyright 2002-2015 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.web.servlet.view.document; + +import java.io.Closeable; +import java.io.IOException; +import java.util.Map; +import javax.servlet.ServletOutputStream; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.apache.poi.hssf.usermodel.HSSFWorkbook; +import org.apache.poi.ss.usermodel.Workbook; + +import org.springframework.web.servlet.view.AbstractView; + +/** + * Convenient superclass for Excel document views in traditional XLS format. + * Compatible with Apache POI 3.5 and higher. + * + *

For working with the workbook in the subclass, see + * Apache's POI site + * + * @author Juergen Hoeller + * @since 4.2 + */ +public abstract class AbstractXlsView extends AbstractView { + + /** + * Default Constructor. + * Sets the content type of the view to "application/vnd.ms-excel". + */ + public AbstractXlsView() { + setContentType("application/vnd.ms-excel"); + } + + + @Override + protected boolean generatesDownloadContent() { + return true; + } + + /** + * Renders the Excel view, given the specified model. + */ + @Override + protected final void renderMergedOutputModel( + Map model, HttpServletRequest request, HttpServletResponse response) throws Exception { + + // Create a fresh workbook instance for this render step. + Workbook workbook = createWorkbook(model, request); + + // Delegate to application-provided document code. + buildExcelDocument(model, workbook, request, response); + + // Set the content type. + response.setContentType(getContentType()); + + // Flush byte array to servlet output stream. + renderWorkbook(workbook, response);; + } + + + /** + * Template method for creating the POI {@link Workbook} instance. + *

The default implementation creates a traditional {@link HSSFWorkbook}. + * Spring-provided subclasses are overriding this for the OOXML-based variants; + * custom subclasses may override this for reading a workbook from a file. + * @param model the model Map + * @param request current HTTP request (for taking the URL or headers into account) + * @return the new {@link Workbook} instance + */ + protected Workbook createWorkbook(Map model, HttpServletRequest request) { + return new HSSFWorkbook(); + } + + /** + * The actual render step: taking the POI {@link Workbook} and rendering + * it to the given response. + * @param workbook the POI Workbook to render + * @param response current HTTP response + * @throws IOException when thrown by I/O methods that we're delegating to + */ + protected void renderWorkbook(Workbook workbook, HttpServletResponse response) throws IOException { + ServletOutputStream out = response.getOutputStream(); + workbook.write(out); + out.flush(); + + // Closeable only implemented as of POI 3.10 + if (workbook instanceof Closeable) { + ((Closeable) workbook).close(); + } + } + + /** + * Application-provided subclasses must implement this method to populate + * the Excel workbook document, given the model. + * @param model the model Map + * @param workbook the Excel workbook to populate + * @param request in case we need locale etc. Shouldn't look at attributes. + * @param response in case we need to set cookies. Shouldn't write to it. + */ + protected abstract void buildExcelDocument( + Map model, Workbook workbook, HttpServletRequest request, HttpServletResponse response) + throws Exception; + +} diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/view/document/AbstractXlsxStreamingView.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/view/document/AbstractXlsxStreamingView.java new file mode 100644 index 00000000000..5f31788ba82 --- /dev/null +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/view/document/AbstractXlsxStreamingView.java @@ -0,0 +1,59 @@ +/* + * Copyright 2002-2015 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.web.servlet.view.document; + +import java.io.IOException; +import java.util.Map; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.apache.poi.ss.usermodel.Workbook; +import org.apache.poi.xssf.streaming.SXSSFWorkbook; + +/** + * Convenient superclass for Excel document views in the Office 2007 XLSX format, + * using POI's streaming variant. Compatible with Apache POI 3.9 and higher. + * + *

For working with the workbook in the subclass, see + * Apache's POI site + * + * @author Juergen Hoeller + * @since 4.2 + */ +public abstract class AbstractXlsxStreamingView extends AbstractXlsxView { + + /** + * This implementation creates a {@link SXSSFWorkbook} for streaming the XLSX format. + */ + @Override + protected SXSSFWorkbook createWorkbook(Map model, HttpServletRequest request) { + return new SXSSFWorkbook(); + } + + /** + * This implementation disposes the {@link SXSSFWorkbook} when done with rendering. + * @see org.apache.poi.xssf.streaming.SXSSFWorkbook#dispose() + */ + @Override + protected void renderWorkbook(Workbook workbook, HttpServletResponse response) throws IOException { + super.renderWorkbook(workbook, response); + + // Dispose temporary files in case of streaming variant... + ((SXSSFWorkbook) workbook).dispose(); + } + +} diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/view/document/AbstractXlsxView.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/view/document/AbstractXlsxView.java new file mode 100644 index 00000000000..2c4b3e5d886 --- /dev/null +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/view/document/AbstractXlsxView.java @@ -0,0 +1,54 @@ +/* + * Copyright 2002-2015 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.web.servlet.view.document; + +import java.util.Map; +import javax.servlet.http.HttpServletRequest; + +import org.apache.poi.ss.usermodel.Workbook; +import org.apache.poi.xssf.usermodel.XSSFWorkbook; + +/** + * Convenient superclass for Excel document views in the Office 2007 XLSX format + * (as supported by POI-OOXML). Compatible with Apache POI 3.5 and higher. + * + *

For working with the workbook in the subclass, see + * Apache's POI site + * + * @author Juergen Hoeller + * @since 4.2 + */ +public abstract class AbstractXlsxView extends AbstractXlsView { + + /** + * Default Constructor. + * Sets the content type of the view to + * "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet". + */ + public AbstractXlsxView() { + setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"); + } + + /** + * This implementation creates an {@link XSSFWorkbook} for the XLSX format. + */ + @Override + protected Workbook createWorkbook(Map model, HttpServletRequest request) { + return new XSSFWorkbook(); + } + +} diff --git a/spring-webmvc/src/test/java/org/springframework/web/servlet/view/document/ExcelViewTests.java b/spring-webmvc/src/test/java/org/springframework/web/servlet/view/document/ExcelViewTests.java index d6cfc81b3aa..c55452b0c8c 100644 --- a/spring-webmvc/src/test/java/org/springframework/web/servlet/view/document/ExcelViewTests.java +++ b/spring-webmvc/src/test/java/org/springframework/web/servlet/view/document/ExcelViewTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2015 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -36,7 +36,6 @@ import org.apache.poi.hssf.usermodel.HSSFCell; import org.apache.poi.hssf.usermodel.HSSFRow; import org.apache.poi.hssf.usermodel.HSSFSheet; import org.apache.poi.hssf.usermodel.HSSFWorkbook; -import org.apache.poi.poifs.filesystem.POIFSFileSystem; import org.junit.Before; import org.junit.Test; @@ -59,8 +58,6 @@ import static org.junit.Assert.*; @SuppressWarnings("deprecation") public class ExcelViewTests { - private MockServletContext servletCtx; - private MockHttpServletRequest request; private MockHttpServletResponse response; @@ -70,7 +67,7 @@ public class ExcelViewTests { @Before public void setUp() { - servletCtx = new MockServletContext("org/springframework/web/servlet/view/document"); + MockServletContext servletCtx = new MockServletContext("org/springframework/web/servlet/view/document"); request = new MockHttpServletRequest(servletCtx); response = new MockHttpServletResponse(); webAppCtx = new StaticWebApplicationContext(); @@ -99,8 +96,7 @@ public class ExcelViewTests { excelView.render(new HashMap(), request, response); - POIFSFileSystem poiFs = new POIFSFileSystem(new ByteArrayInputStream(response.getContentAsByteArray())); - HSSFWorkbook wb = new HSSFWorkbook(poiFs); + HSSFWorkbook wb = new HSSFWorkbook(new ByteArrayInputStream(response.getContentAsByteArray())); assertEquals("Test Sheet", wb.getSheetName(0)); HSSFSheet sheet = wb.getSheet("Test Sheet"); HSSFRow row = sheet.getRow(2); @@ -134,8 +130,7 @@ public class ExcelViewTests { excelView.setUrl("template"); excelView.render(new HashMap(), request, response); - POIFSFileSystem poiFs = new POIFSFileSystem(new ByteArrayInputStream(response.getContentAsByteArray())); - HSSFWorkbook wb = new HSSFWorkbook(poiFs); + HSSFWorkbook wb = new HSSFWorkbook(new ByteArrayInputStream(response.getContentAsByteArray())); HSSFSheet sheet = wb.getSheet("Sheet1"); HSSFRow row = sheet.getRow(0); HSSFCell cell = row.getCell(0); @@ -168,8 +163,7 @@ public class ExcelViewTests { excelView.setUrl("template"); excelView.render(new HashMap(), request, response); - POIFSFileSystem poiFs = new POIFSFileSystem(new ByteArrayInputStream(response.getContentAsByteArray())); - HSSFWorkbook wb = new HSSFWorkbook(poiFs); + HSSFWorkbook wb = new HSSFWorkbook(new ByteArrayInputStream(response.getContentAsByteArray())); HSSFSheet sheet = wb.getSheet("Sheet1"); HSSFRow row = sheet.getRow(0); HSSFCell cell = row.getCell(0); @@ -202,8 +196,7 @@ public class ExcelViewTests { excelView.setUrl("template"); excelView.render(new HashMap(), request, response); - POIFSFileSystem poiFs = new POIFSFileSystem(new ByteArrayInputStream(response.getContentAsByteArray())); - HSSFWorkbook wb = new HSSFWorkbook(poiFs); + HSSFWorkbook wb = new HSSFWorkbook(new ByteArrayInputStream(response.getContentAsByteArray())); HSSFSheet sheet = wb.getSheet("Sheet1"); HSSFRow row = sheet.getRow(0); HSSFCell cell = row.getCell(0); diff --git a/spring-webmvc/src/test/java/org/springframework/web/servlet/view/document/XlsViewTests.java b/spring-webmvc/src/test/java/org/springframework/web/servlet/view/document/XlsViewTests.java new file mode 100644 index 00000000000..d99e0d67f87 --- /dev/null +++ b/spring-webmvc/src/test/java/org/springframework/web/servlet/view/document/XlsViewTests.java @@ -0,0 +1,121 @@ +/* + * Copyright 2002-2015 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.web.servlet.view.document; + +import java.io.ByteArrayInputStream; +import java.util.HashMap; +import java.util.Map; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.apache.poi.hssf.usermodel.HSSFWorkbook; +import org.apache.poi.ss.usermodel.Cell; +import org.apache.poi.ss.usermodel.Row; +import org.apache.poi.ss.usermodel.Sheet; +import org.apache.poi.ss.usermodel.Workbook; +import org.apache.poi.xssf.usermodel.XSSFWorkbook; +import org.junit.Test; + +import org.springframework.mock.web.test.MockHttpServletRequest; +import org.springframework.mock.web.test.MockHttpServletResponse; +import org.springframework.web.servlet.View; + +import static org.junit.Assert.*; + +/** + * Tests for AbstractXlsView and its subclasses. + * + * @author Juergen Hoeller + * @since 4.2 + */ +public class XlsViewTests { + + private final MockHttpServletRequest request = new MockHttpServletRequest(); + + private final MockHttpServletResponse response = new MockHttpServletResponse(); + + + @Test + public void testXls() throws Exception { + View excelView = new AbstractXlsView() { + @Override + protected void buildExcelDocument(Map model, Workbook workbook, + HttpServletRequest request, HttpServletResponse response) throws Exception { + Sheet sheet = workbook.createSheet("Test Sheet"); + Row row = sheet.createRow(0); + Cell cell = row.createCell(0); + cell.setCellValue("Test Value"); + } + }; + + excelView.render(new HashMap(), request, response); + + Workbook wb = new HSSFWorkbook(new ByteArrayInputStream(response.getContentAsByteArray())); + assertEquals("Test Sheet", wb.getSheetName(0)); + Sheet sheet = wb.getSheet("Test Sheet"); + Row row = sheet.getRow(0); + Cell cell = row.getCell(0); + assertEquals("Test Value", cell.getStringCellValue()); + } + + @Test + public void testXlsxView() throws Exception { + View excelView = new AbstractXlsxView() { + @Override + protected void buildExcelDocument(Map model, Workbook workbook, + HttpServletRequest request, HttpServletResponse response) throws Exception { + Sheet sheet = workbook.createSheet("Test Sheet"); + Row row = sheet.createRow(0); + Cell cell = row.createCell(0); + cell.setCellValue("Test Value"); + } + }; + + excelView.render(new HashMap(), request, response); + + Workbook wb = new XSSFWorkbook(new ByteArrayInputStream(response.getContentAsByteArray())); + assertEquals("Test Sheet", wb.getSheetName(0)); + Sheet sheet = wb.getSheet("Test Sheet"); + Row row = sheet.getRow(0); + Cell cell = row.getCell(0); + assertEquals("Test Value", cell.getStringCellValue()); + } + + @Test + public void testXlsxStreamingView() throws Exception { + View excelView = new AbstractXlsxStreamingView() { + @Override + protected void buildExcelDocument(Map model, Workbook workbook, + HttpServletRequest request, HttpServletResponse response) throws Exception { + Sheet sheet = workbook.createSheet("Test Sheet"); + Row row = sheet.createRow(0); + Cell cell = row.createCell(0); + cell.setCellValue("Test Value"); + } + }; + + excelView.render(new HashMap(), request, response); + + Workbook wb = new XSSFWorkbook(new ByteArrayInputStream(response.getContentAsByteArray())); + assertEquals("Test Sheet", wb.getSheetName(0)); + Sheet sheet = wb.getSheet("Test Sheet"); + Row row = sheet.getRow(0); + Cell cell = row.getCell(0); + assertEquals("Test Value", cell.getStringCellValue()); + } + +}