jXLSã使ã£ãExcelãã³ãã¬ã¼ããSpring MVCã®ãã¥ã¼ã¨ãã¦å©ç¨ããæ¹æ³
jXLSã使ã£ã¦JSPã¨åæ§ã®æ¹æ³ã§Excelãã¡ã¤ã«ãçæãã
æ¥åã¢ããªã±ã¼ã·ã§ã³ã§ã¯å¥½ãã¨å¥½ã¾ããã¨ã«ããããããExcelãã¡ã¤ã«ã®å
¥åºåãè¡ãå¿
è¦ãããå ´åãå¤ãããã¾ããJavaããExcelãã¡ã¤ã«ã®èªã¿æ¸ããè¡ãOSSã®ã©ã¤ãã©ãªã¼ã¯ããã¤ãããã¾ãããä¸ã§ãApache POIãæåã§ãã以åã¯ä¸å®å®ã ã£ãããæ©è½ãéããã¦ãããããã¨ãããããã®ã§ãããé·ãæéãããã¦ç¾å¨ã§ã¯ããªãå®å®ãã¦ä½¿ããã©ã¤ãã©ãªã¼ã«ãªã£ã¦ãã¾ãã
ãã ããPOIã®æ大ã®åé¡ç¹ã¯ãæä¾ããã¦ããAPIããã¾ãã«ãä½æ°´æºã§ãããã¨ã§ããåç´ãªExcelãã¡ã¤ã«ã®å¸³ç¥¨ãä½æããã ãã§ããå¤éã«ã¼ããæ¸ããªããã»ã«ãã¨ã«å¤ã転è¨ãããããªé¢åãªã³ã¼ãã£ã³ã°ãå¿
è¦ã«ãªãã¾ãã
Excelã®ãã³ãã¬ã¼ããã¡ã¤ã«ãä½æãã¦ãããããã«å¤ããã¤ã³ããããã¨ã§Excel帳票ãçæãããããªã±ã¼ã¹ã§ã¯ãjXLSã¨ããã©ã¤ãã©ãªã¼ãå©ç¨ããã¨ä¾¿å©ã§ãã*1ã³ã³ã»ããã¯é常ã«ãããããããã¡ããã©JSPãã¡ã¤ã«ã使ã£ã¦ç»é¢ãã³ãã¬ã¼ããå®ç¾©ããã®ã¨åããããªæè¦ã§ãExcelã®ãã³ãã¬ã¼ããä½æããããã«åçãªå¤ããã¤ã³ãããããã¨ãã§ããããã«ãªã£ã¦ãã¾ãã
JSPã¨åãæ¹æ³ã§Excelãã¡ã¤ã«çæãè¡ãæ¹æ³ãæ¤è¨ããï¼å¤é¨ä»æ§ï¼
Spring MVCã§ã¯æ¨æºã§ãPOIã使ã£ãExcelãã¡ã¤ã«çæç¨ã®ãã¥ã¼ã¯ã©ã¹ï¼AbstractExcelViewï¼ãæä¾ããã¦ãã¾ããããã®ã¯ã©ã¹ã¯æ½è±¡ã¯ã©ã¹ã§ãåExcelãã¡ã¤ã«ãã¨ã«ãµãã¯ã©ã¹ãä½æããå¿
è¦ãããã¾ããjXLSã§ã¯JSPã¨åãããã°ã©ãã³ã°ã¢ãã«ã§Excelãã¡ã¤ã«ãçæã§ããã®ã§ããããã§ããã ãJSPã¨åæ§ã®æ¹æ³ã§éçºã§ãããããã§ãããããã§ã¯ãSpring MVCã«ã¡ãã£ã¨ããæ¡å¼µãããã¨ã§ããããã£ãã·ã¼ã ã¬ã¹ãªé£æºãå¯è½ã«ããæ¹æ³ãç´¹ä»ãã¾ãã
JSPã¨åãæ¹æ³ã§Excelãä½æããããã«ãåã
ã®ãã³ãã¬ã¼ãã¨ãªãExcelãã¡ã¤ã«ã以ä¸ã®ããã«viewãã©ã«ãã«é
ç½®ãããã¨ã«ãã¾ãã
次ã«ãã³ã³ããã¼ã©ã¼ã§ãJSPãã¥ã¼ã«è¡¨ç¤ºãããã®ããExcelãã¥ã¼ã«è¡¨ç¤ºãããã®ããèå¥ããããã@Jxlsã¨ããã¢ããã¼ã·ã§ã³ãä½æããã¢ã¯ã·ã§ã³ã¡ã½ããããã¼ã¯ä»ãã§ããããã«ãã¾ããã¾ãã@Jxlsã§ã¯filenameå±æ§ã§ãã¦ã³ãã¼ããããã¡ã¤ã«åãæå®ã§ããããã«ãã¾ãã
@Controller @RequestMapping("/jxls") public class JxlsController { @RequestMapping("/department") @Jxls(filename = "é¨ç½²ã¬ãã¼ã.xls") public Department department() { return buildSampleDepartment(); } ... }
ãã®ããã«ããããé常ã®department.jspã§ã¯ãªãdepartment.xlsã®ãã³ãã¬ã¼ãããã¦ã³ãã¼ããããããã«ãããã¨æãã¾ãã
ãªãã念ã®ãããä¸è¨ã®ã³ã³ããã¼ã©ã®è¨è¿°æ¹æ³ã¯CoCã使ã£ãç°¡æè¨æ³ã§ããå®éã«ã¯ã以ä¸ã¨ç価ã§ããã¨èãã¦ãã ãããã¤ã¾ããã³ã³ããã¼ã©ã¼ã®ã¡ã½ããã®æ»ãå¤ã§String以å¤ã®ãªãã¸ã§ã¯ããè¿ããå ´åããã¥ã¼åï¼/jxls/departmentï¼ã¯ãªã¯ã¨ã¹ãURLããèªåçã«æ±ºã¾ããã¢ãã«ã®ãã¼ï¼departmentï¼ãDepartmentã¨ããåããé¡æ¨ããã¾ãã
@Controller @RequestMapping("/jxls") public class JxlsController { @RequestMapping("/department") @Jxls(filename = "é¨ç½²ã¬ãã¼ã.xls") public String department(Model model) { model.addAttribute("department", buildSampleDepartment()); return "/jxls/department"; } ... }
Spring MVCãæ¡å¼µããç¬èªã®Viewã¨ViewResolverã®å®è£ ãä½æãã
以ä¸ã®ãããªåä½ãå®ç¾ãããããã«ã以ä¸ã®ãã¨ãå¿ è¦ã«ãªãã¾ãã
- Jxlsã«åºã¥ãã¦ãã³ãã¬ã¼ãExcelããExcelãã¡ã¤ã«ãçæãããã¥ã¼ã¯ã©ã¹ãJxlsViewã¨ãã¦ä½æãã
- @Jxlsã¨ããã¢ããã¼ã·ã§ã³ãã¡ã½ããã«ã¤ãã¦ããå ´åã«JxlsViewã«å¦çãæ¯ãåããJxlsViewResolverãä½æãã
ã¾ãããã®ããã«Spring MVCãæ¡å¼µãããå ´åãæåããæä¾ããã¦ããã¯ã©ã¹ã§åèã«ãªããããªãã®ã¯ãªããæ¢ãã®ã第ä¸æ©ã§ãããã¨ãã°ãIDEã®ã¯ã©ã¹é層表示æ©è½ã§ãViewResolverã®é層ã表示ãã¦ã¿ãã¨ä»¥ä¸ã®ããã«ãªãã¾ãã
ããã¯ãããç¨åº¦ãã¬ã¼ã ã¯ã¼ã¯æ¡å¼µãè¡ãããã®åãå¿
è¦ã«ãªãã¨ããã§ãããJSPã®ãã¥ã¼ãå¦çãã¦ããInternalResourceViewResolverãPDFã®ãã¡ã¤ã«ãå¦çãã¦ããRasperReportViewResolverãããã®å®è£
ãåèã«ãªãã®ã§ã¯ãªããã¨ããããã¤ãã¾ãããããã®ãã¥ã¼ã¯ã¨ãã«UrlBasedViewResolverã¨ããæ½è±¡ã¯ã©ã¹ãç¶æ¿ãã¦ä½æããã¦ãããã³ã³ããã¹ãã«ã¼ãã«æ ¼ç´ããããã¡ã¤ã«ã«å¯¾ããURLããç»é¢ãçæããããã¥ã¼ã¨ãªã£ã¦ãã¾ããããã§ããããã®ã¯ã©ã¹ã®å
å¼ã¯ã©ã¹ã¨ãã¦ãJxlsViewResolverã以ä¸ã®ããã«ä½æããã°ãããã¨ããããã¾ãã
public class JxlsViewResolver extends UrlBasedViewResolver { public JxlsViewResolver() { setViewClass(JxlsView.class); } @Override protected Class<JxlsView> requiredViewClass() { return JxlsView.class; } @Override protected boolean canHandle(String viewName, Locale locale) { Method currentHandlerMethod = Controllers.getCurrentHandlerMethod(); return currentHandlerMethod != null && currentHandlerMethod.isAnnotationPresent(Jxls.class) && super.canHandle(viewName, locale); } }
ãªãããã®ã¯ã©ã¹ã§ã¯ãSpring MVCでコントローラーのリクエストハンドラメソッドのメタ情報を記録する方法 - 達人プログラマーを目指してã§ç´¹ä»ãããã¯ããã¯ã使ã£ã¦ãã³ã³ããã¼ã©ã¼ã®ã¡ã½ããã®ã¢ããã¼ã·ã§ã³ãåå¾ãã¦ãã¾ããcanHandle()ã¡ã½ããã§ã¯ãã³ã³ããã¼ã©ã¼ã«@Jxlsã¢ããã¼ã·ã§ã³ãä»ãã¦ããå ´åã®ã¿ãtrueãè¿ããã¨ã§ããã以å¤ã®å ´åã¯ãã®ä»ã®ViewResolverã«å¦çãå§è²ãããããã«å®è£
ãã¦ãã¾ãã
次ã«ãJxlsViewã®å®è£
ä¾ã以ä¸ã«ç¤ºãã¾ãã
import java.io.IOException; import java.io.UnsupportedEncodingException; import java.lang.reflect.Method; import java.net.URLEncoder; import java.util.Locale; import java.util.Map; import javax.servlet.ServletOutputStream; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import net.sf.jxls.transformer.XLSTransformer; 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; import org.springframework.web.servlet.support.RequestContextUtils; import org.springframework.web.servlet.view.AbstractUrlBasedView; import com.sun.xml.internal.messaging.saaj.packaging.mime.internet.MimeUtility; public class JxlsView extends AbstractUrlBasedView { private static final String CONTENT_TYPE = "application/vnd.ms-excel"; private static final String EXTENSION = ".xls"; public JxlsView() { setContentType(CONTENT_TYPE); } @Override protected boolean generatesDownloadContent() { return true; } @Override protected final void renderMergedOutputModel(Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) throws Exception { HSSFWorkbook workbook = createWorkbook(request); buildExcelDocument(model, workbook, request, response); setupHeader(model, request, response); doRender(response, workbook); } protected void setupHeader(Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) throws UnsupportedEncodingException { // Set the content type. response.setContentType(getContentType()); setupHeaderForDownloadFilename(model, request, response); } protected void setupHeaderForDownloadFilename(Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) throws UnsupportedEncodingException { Method currentHandlerMethod = Controllers.getCurrentHandlerMethod(); assert currentHandlerMethod != null; Jxls jxls = currentHandlerMethod.getAnnotation(Jxls.class); assert jxls != null; String filename = jxls.filename(); if (model.containsKey(filename)) { filename = model.get(filename).toString(); } response.setHeader("Content-Disposition", "attachment; filename=" + encodeFilename(filename, request)); } // TODO extract a strategy class. private String encodeFilename(String filename, HttpServletRequest request) throws UnsupportedEncodingException { if (request.getHeader("User-Agent").indexOf("MSIE") == -1) { return MimeUtility.encodeWord(filename, "ISO-2022-JP", "B"); // FIXME Japanese encoding is hard coded. } else { // for legacy IE6 return URLEncoder.encode(filename, "UTF-8"); } } private HSSFWorkbook createWorkbook(HttpServletRequest request) throws Exception { HSSFWorkbook workbook; if (getUrl() != null) { workbook = getTemplateSource(getUrl(), request); } else { workbook = new HSSFWorkbook(); logger.debug("Created Excel Workbook from scratch"); } return workbook; } private void doRender(HttpServletResponse response, HSSFWorkbook workbook) throws IOException { // Flush byte array to servlet output stream. ServletOutputStream out = response.getOutputStream(); workbook.write(out); out.flush(); } protected HSSFWorkbook getTemplateSource(String url, HttpServletRequest request) throws Exception { LocalizedResourceHelper helper = new LocalizedResourceHelper(getApplicationContext()); Locale userLocale = RequestContextUtils.getLocale(request); Resource inputFile = helper.findLocalizedResource(url, url.endsWith(EXTENSION) ? "" : EXTENSION, userLocale); POIFSFileSystem fs = new POIFSFileSystem(inputFile.getInputStream()); return new HSSFWorkbook(fs); } protected void buildExcelDocument(Map<String, Object> model, HSSFWorkbook workbook, HttpServletRequest request, HttpServletResponse response) throws Exception { XLSTransformer transformer = new XLSTransformer(); transformer.transformWorkbook(workbook, model); } }
ãã®ã¯ã©ã¹ã¯AbstractUrlBasedViewãç¶æ¿ããå¿ è¦ãããã®ã§ãããJavaã§ã¯å¤éç¶æ¿ãMixinãã§ããªããããæ®å¿µãªããAbstractExcelViewã®å®è£ ãä¸é¨ã³ãããããã¨ã§å¯¾å¿ãã¦ãã¾ãããã®ã¯ã©ã¹ã§ãã£ã¨ã大åãªãã¤ã³ãã¯buildExcelDocument()ã¡ã½ããã®é¨åã§ãããã§jXLSã使ã£ã¦Excelãã³ãã¬ã¼ãã«å¯¾ããå¤ã®æ¸ãè¾¼ã¿ãå®è¡ãã¦ãã¾ãã
Spring MVCã®Beanå®ç¾©ãã¡ã¤ã«ã®è¨å®
以ä¸ã§ä½æããViewResolverã使ãããã«ãBeanå®ç¾©ãã¡ã¤ã«ã«ä»¥ä¸ã追è¨ãã¾ãã
<bean class="com.github.ryoasai.spring_jxsl.JxlsViewResolver"> <property name="prefix" value="/WEB-INF/views/" /> <property name="suffix" value=".xls" /> <property name="order" value="1" /> </bean> <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="prefix" value="/WEB-INF/views/" /> <property name="suffix" value=".jsp" /> <property name="order" value="2" /> </bean>
ããã§ã¯ãChain of Responsibilityãã¿ã¼ã³ã使ã£ã¦ãå
ã»ã©å®ç¾©ããExcelç¨ã®ãã¥ã¼ã¬ã¾ã«ãã¼ã¨JSPç¨ã®ãã¥ã¼ã¬ã¾ã«ãã¼ãä½µç¨ããè¨å®ã¨ãªã£ã¦ãã¾ããorderå±æ§ãæå®ãããã¨ããã¤ã³ãã§ããã®æ°å¤ãä½ãã»ã©ãã¥ã¼ãåªå
çã«è§£æ±ºãããããã«ãªãã¾ãã
ãªãã以ä¸ã®ãµã³ãã«ã³ã¼ããå«ãã¦ãã½ã¼ã¹ã³ã¼ãã¯GitHub - ryoasai/spring-mvc-exts: Some extensions to the Spring MVC web application framework.ã«å
¬éãã¦ãã¾ãã®ã§ãã©ãããèªç±ã«ãå©ç¨ãã ããã
*1:ä¼¼ããããªã³ã³ã»ããã®ã©ã¤ãã©ãªã¼ã¨ãã¦ã¯ãå½ç£ã®Fisshplateã¨ããã®ãããã¾ããç§ã¯ã¾ã 試ãã¦ãã¾ãããã