打开APP
userphoto
未登录

开通VIP,畅享免费电子书等14项超值服

开通VIP
Spring3.X @MVC

在Spring3.X @MVC - (七)Spring中强大的内容协商视图解析器的基础上,

http://josh-persistence.iteye.com/blog/1884206

来学习怎样在Spring中创建和导出Excel和PDF的视图。

1. 问题:

尽管HTML是显示Web内容最常见的方法,但是有时候你的用户希望从Web应用中导出Excel或者PDF格式的内容。在java中,有许多的的程序库有助于生成Excel和PDF文件。但是,为了在Web应用中直接使用这些程序库,你必须在后台生成这些文件,然后将它们作为二进制返回给用户。为此你必须处理HTTP的响应头标和输出流。

2.解决方案:

Spring将Excel和PDF文件的生成集成到MVC框架中。你可以将Excel和PDF文件看作特殊类型的视图,因此你就可以在控制器中一致性地处理web请求,并将数据添加到一个传递给Excel和PDF视图的模式中。这样,你就没有必要再去考虑处理复杂的HTTP响应头标和输出流了。

Spring MVC支持使用Apache POI程序库(http://poi.apache.org)或者JExcel API程序库(http://jexcelapi.sourceforge.net)生成Excel文件。对应的视图类分别是AbstractExceView和AbstractJExcelView。PDF文件由IText程序库(http://www.lowagie.com/iText/)生成,对应的视图类是AbstractPdfView类。

3. 工作原理:

假定你的用户希望生成特定日期的预定摘要报告。他们希望这个报告可以以Excel、PDF或者基本的HTML格式生成。为了这个报告的生成功能,你可以在service层定义一个方法,返回具体日期的所有预定:

public interface ReservationService {

public List<Reservation> findByDate(Date date);

}

然后为这个方法提供一个简单的实现,枚举所有的预定:

public class ReservationServiceImpl implements ReservationService {

public List<Reservation> findByDate(Date date) {

List<Reservation> result = new ArrayList<Reservation>();

for (Reservation reservation : reservations) {

if (reservation.getDate().equals(date)) {

result.add(reservation);

}

}

return result;

}

接下来我们可以编写一个简单的控制器,该控制器会从URL请求中获取date,然后将date格式化为一个日期对象并且传递给服务层查询预订。控制器依赖上一节中所讲的内容协商视图解析器,因此控制器会返回一个逻辑视图,然后由解析器决定生成Excel, PDF,还是默认的HTML网页。

package com.wsheng.spring.web;

...

@Controller

@RequestMapping("/reservationSummary*")

public class ReservationSummaryController {

private ReservationService reservationService;

@Autowired

public ReservationSummaryController(ReservationService reservationService) {

this.reservationService = reservationService;

}

@RequestMapping(method = RequestMethod.GET)

public String generateSummary(

@RequestParam(required = true, value = "date") String selectedDate, Model model) {

List<Reservation> reservations = java.util.Collections.emptyList();

// Format date

try {

Date summaryDate = new SimpleDateFormat("yyyy-MM-dd").parse(selectedDate);

reservations = reservationService.findByDate(summaryDate);

// Catch error if request parameter date is not in format

} catch (java.text.ParseException ex) {

StringWriter sw = new StringWriter();

PrintWriter pw = new PrintWriter(sw);

ex.printStackTrace(pw);

throw new ReservationWebException("Invalid date format for reservation summary",new Date(),sw.toString());

}

model.addAttribute("reservations",reservations);

return "reservationSummary";

}

}

你可能注意到了,尽管我们设计该控制器的初衷是需要支持PDF,XLS和HTML视图,但是代码中只返回单一的视图(return "reservationSummary"),这是由ContentNegotiatingViewResolver解析器根据这个视图的名称确定使用哪一个视图。更多关于这个解析器的信息,你可以参照我的上一节博客:http://josh-persistence.iteye.com/blog/1884206

创建Excel视图

Excel视图可以通过扩展AbstractExcelView(对于Apache POI)或者AbstractJExcelView(对于JExcel API)来创建。这里以POI及AbstractExcelView为例,在buildExcelDocument()方法中,你可以访问到Sping MVC控制器传递过来的模式Model和一个预先创建好的Excel工作薄,然后你所需要写的代码就是用模式中的数据来填充这个工作薄。在Maven中引入poi的dependency:

<dependency>

<groupId>org.apache.poi</groupId>

<artifactId>poi</artifactId>

<version>3.0.2-FINAL</version>

</dependency>

package com.wsheng.spring.web.view;

...

public class ExcelReservationSummary extends AbstractExcelView {

protected void buildExcelDocument(Map model, HSSFWorkbook workbook,

HttpServletRequest request, HttpServletResponse response)

throws Exception {

List<Reservation> reservations = (List) model.get("reservations");

DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");

HSSFSheet sheet = workbook.createSheet();

HSSFRow header = sheet.createRow(0);

header.createCell((short) 0).setCellValue("Court Name");

header.createCell((short) 1).setCellValue("Date");

header.createCell((short) 2).setCellValue("Hour");

header.createCell((short) 3).setCellValue("Player Name");

header.createCell((short) 4).setCellValue("Player Phone");

int rowNum = 1;

for (Reservation reservation : reservations) {

HSSFRow row = sheet.createRow(rowNum++);

row.createCell((short) 0).setCellValue(reservation.getCourtName());

row.createCell((short) 1).setCellValue(

dateFormat.format(reservation.getDate()));

row.createCell((short) 2).setCellValue(reservation.getHour());

row.createCell((short) 3).setCellValue(

reservation.getPlayer().getName());

row.createCell((short) 4).setCellValue(

reservation.getPlayer().getPhone());

}

}

}

因为我们前面在控制器中配置了@RequestMapping("/reservationSummary*"),URL请求中需要一个required的参数,date,所以可以这样访问Excel视图:

http://localhost:8088/wsheng-spring-mvc/reservationSummary.xlsdate=2013-06-21 可以下载或直接打开相关的excel,里面应该有2条值。

http://localhost:8088/wsheng-spring-mvc/reservationSummary.xlsdate=20130621 可以看到页面上显示相应的异常信息。

http://localhost:8088/wsheng-spring-mvc/reservationSummary.xlsdate=2013-07-21可以下载或直接打开相关的excel,里面没有header信息,没有值。

创建PDF视图

PDF视图通过扩展AbstractPdfView类来创建。在buildPdfDocument()方法中,你可以访问控制器传递过来的模式Model和一个预先创建的PDF文档。然后将模式中的数据填充到该PDF文档中。添加itext的dependency

<dependency>

<groupId>com.lowagie</groupId>

<artifactId>itext</artifactId>

<version>2.0.8</version>

</dependency>

package com.wsheng.spring.web.view;

...

public class PdfReservationSummary extends AbstractPdfView {

protected void buildPdfDocument(Map model, Document document,

PdfWriter writer, HttpServletRequest request,

HttpServletResponse response) throws Exception {

List<Reservation> reservations =

(List<Reservation>) model.get("reservations");

DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");

Table table = new Table(5);

table.addCell("Court Name");

table.addCell("Date");

table.addCell("Hour");

table.addCell("Player Name");

table.addCell("Player Phone");

if (!reservations.isEmpty()) {

for (Reservation reservation : reservations) {

table.addCell(reservation.getCourtName());

table.addCell(dateFormat.format(reservation.getDate()));

table.addCell(Integer.toString(reservation.getHour()));

table.addCell(reservation.getPlayer().getName());

table.addCell(reservation.getPlayer().getPhone());

}

}

document.add(table);

}

}

因为我们前面在控制器中配置了@RequestMapping("/reservationSummary*"),URL请求中需要一个required的参数,date,所以可以这样访问PDF视图:

http://localhost:8088/wsheng-spring-mvc/reservationSummary.pdfdate=2013-06-21

为Excel和PDF视图创建视图解析器

在前面几篇博客中,我们知道了"根据名称解析视图”,知道了怎样将MVC控制器中返回的逻辑视图名称解析为具体视图的不同的策略。这里我们选择用资源集(properties)文件的方式,即将对PDF和XLS类别的视图的映射的配置放在properties中。

1. 在Web应用上下文中(court-servlet.xml)中配置ResourceBundleViewResolver bean作为视图解析器。

2. 确保在Web应用的classpath根目录下有views.properties和secondaryviews.properties.

在views.properties中包含:

reservationSummary.(class)=com.wsheng.spring.web.view.ExcelReservationSummary

在secondaryviews.properties中包含:

reservationSummary.(class)=com.wsheng.spring.web.view.PdfReservationSummary

在控制器中返回的逻辑视图名称是reservationSummary。ContentNegotiatingViewResolver解析器的任务是根据用户的请求确定使用哪一个各类。一旦确定了这一点,执行对应的类生成PDF或者XLS文件。

此时你可能已经发现无论是你使用http://localhost:8088/wsheng-spring-mvc/reservationSummary.xlsdate=2013-06-21

或者使用http://localhost:8088/wsheng-spring-mvc/reservationSummary.pdfdate=2013-06-21

此时浏览器提示的都是保存或者打开reservationSummary.pdf或者reservationSummary.xls这种命名习惯是基于用户请求资源的URL的。但是,我们还在URL中提供了日期的信息,如果能自动保存为reservationSummary_2013_06_21.xls或者reservationSummary_2013_06_21.pdf,就是一个很好的功能。为了实现这个功能,我们可以通过一个拦截器来重写返回的URL。

public class ExtensionInterceptor extends HandlerInterceptorAdapter {

public void postHandle(HttpServletRequest request,

HttpServletResponse response, Object handler,

//Model model) throws Exception {

ModelAndView modelAndView) throws Exception {

// Report date is present in request

String reportName = null;

String reportDate = request.getQueryString().replace("date=","").replace("-","_");

if (request.getServletPath().endsWith(".pdf")) {

reportName= "ReservationSummary_" + reportDate + ".pdf";

}

if (request.getServletPath().endsWith(".xls")) {

reportName= "ReservationSummary_" + reportDate + ".xls";

}

// ONLY if its a PDF or XLS extension rewrite response URL

// If reportName name was modified, its PDF or XLS

if (reportName != null) {

// Set "Content-Disposition" HTTP Header so a user gets a pretty 'Save as' address

response.setHeader("Content-Disposition","attachment; filename="+reportName);

}

}

}

1. request.getQueryString()返回的是请求url中”?“后面的内容。

2. request.getServletPath()返回的是请求url中""前面的内容。

3. 为了确保用户接收到下载提示,用相应的文件名的名称的格式(pdf/xls)来设置Content-Disposition HTTP头标。

在web上下文中,配置该Interceptor,order为0即优先级最高,当url请求中有reservationSummary的时候,由该interceptor处理。

<bean id="publicMapper" class="org.springplugins.web.SelectedAnnotationHandlerMapping">

<property name="order" value="0" />

<property name="urls">

<list>

<value>/reservationSummary*</value>

</list>

</property>

<property name="interceptors">

<list>

<ref bean="summaryReportInterceptor" />

</list>

</property>

</bean>

总结: 尽管这个应用中使用了ContentNegotiatingViewResolver解析器选择合适的视图,但是修改返回URL的过程超出了视图解析器的范围。因此,有必要使用拦截器人工检查请求扩展名,并且设置必要的HTTP头标,修改输出的URL。

本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请点击举报
打开APP,阅读全文并永久保存 查看更多类似文章
猜你喜欢
类似文章
EXCEL--字符串函数
JSP生成EXCEL文件
使用NPOI导出数据库到Excel文件 - ゞ追忆o0ゞ - 博客园
通过POI读写Excel文件
C#创建Excel(.xls和.xlsx)文件的三种方法
“Java+POI+模板”一:打造复杂Excel 报表
更多类似文章 >>
生活服务
热点新闻
分享 收藏 导长图 关注 下载文章
绑定账号成功
后续可登录账号畅享VIP特权!
如果VIP功能使用有故障,
可点击这里联系客服!

联系客服