springboot 生成各个版本excel和pdf以及遇到的问题
springboot生成excel和pdf网上的资料并不是很多,大多数是springmvc生成excel和pdf的例子.或者是使用Response输出流直接去写,其实这里要介绍的是spring框架已经为我们考虑好了.了解springmvc组件结构的肯定知道view视图解析器组件,这里主要介绍的就是这个组件的实际应用并附上源代码以及使用过程中遇到的坑.
主要介绍的组件:
1. AbstractXlsView 旧版excel下载
2. AbstractXlsxView 新版excel下载
3. AbstractXlsxStreamingView excel大文件下载
4. AbstractPdfView pdf下载
4. PdfStreamingView pdf模板文件下载
maven依赖
1 | <!-- pdf start --> |
旧版Excel(AbstractXlsView) - XlsView.java
1 | package com.data2_0.view; |
新版Excel(AbstractXlsxView) - XlsxView.java
1 | package com.data2_0.view; |
大文件Excel(AbstractXlsxStreamingView) - XlsxStreamingView.java
1 | package com.data2_0.view; |
普通pdf下载
因为springboot中的视图解析器AbstractPdfView使用的是lowagie,这个东西对中文的支持有问题。但是视图解析器是封装好的,所以需要重构这个类。AbstractIText5PdfView.java这个类和原springboot封装的一样可以拷贝过来,调整下引用类(itextpdf)就行了.1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57package com.data2_0.view;
import com.itextpdf.text.Document;
import com.itextpdf.text.DocumentException;
import com.itextpdf.text.PageSize;
import com.itextpdf.text.pdf.PdfWriter;
import org.springframework.web.servlet.view.AbstractView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.ByteArrayOutputStream;
import java.io.OutputStream;
import java.util.Map;
/**
* Created by xiehui1956(@)gmail.com on 17-4-14.
*/
public abstract class AbstractIText5PdfView extends AbstractView {
public AbstractIText5PdfView() {
setContentType("application/pdf");
}
@Override
protected boolean generatesDownloadContent() {
return true;
}
@Override
protected final void renderMergedOutputModel(
Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) throws Exception {
// IE workaround: write into byte array first.
ByteArrayOutputStream baos = createTemporaryOutputStream();
// Apply preferences and build metadata.
Document document = newDocument();
PdfWriter writer = newWriter(document, baos);
prepareWriter(model, writer, request);
buildPdfMetadata(model, document, request);
// Build PDF document.
document.open();
buildPdfDocument(model, document, writer, request, response);
document.close();
// Flush to HTTP response.
writeToResponse(response, baos);
}
protected Document newDocument() {
return new Document(PageSize.A4);
}
protected PdfWriter newWriter(Document document, OutputStream os) throws DocumentException {
return PdfWriter.getInstance(document, os);
}
protected void prepareWriter(Map<String, Object> model, PdfWriter writer, HttpServletRequest request)
throws DocumentException {
writer.setViewerPreferences(getViewerPreferences());
}
protected int getViewerPreferences() {
return PdfWriter.ALLOW_PRINTING | PdfWriter.PageLayoutSinglePage;
}
protected void buildPdfMetadata(Map<String, Object> model, Document document, HttpServletRequest request) {
}
protected abstract void buildPdfDocument(Map<String, Object> model, Document document, PdfWriter writer,
HttpServletRequest request, HttpServletResponse response) throws Exception;
}
Pdf生成工具类PDFUtil.java 这个是在网上找的一个哥们写的感觉不错
1 | package com.data2_0.utils; |
真正业务处理的视图解析器PdfView.java
1 | package com.data2_0.view; |
模板pdf下载
生成插件的中文支持和普通pdf生成类似,这里不再描述.
AbstractPdf5StamperView.java拷贝AbstractPdfStamperView.java调整类引用1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38package com.data2_0.view;
import com.itextpdf.text.pdf.PdfReader;
import com.itextpdf.text.pdf.PdfStamper;
import org.springframework.web.servlet.view.AbstractUrlBasedView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.Map;
/**
* Created by xiehui1956(@)gmail.com on 17-4-14.
*/
public abstract class AbstractPdf5StamperView extends AbstractUrlBasedView {
public AbstractPdf5StamperView() {
setContentType("application/pdf");
}
@Override
protected boolean generatesDownloadContent() {
return true;
}
@Override
protected final void renderMergedOutputModel(
Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) throws Exception {
// IE workaround: write into byte array first.
ByteArrayOutputStream baos = createTemporaryOutputStream();
PdfReader reader = readPdfResource();
PdfStamper stamper = new PdfStamper(reader, baos);
mergePdfDocument(model, stamper, request, response);
stamper.close();
// Flush to HTTP response.
writeToResponse(response, baos);
}
protected PdfReader readPdfResource() throws IOException {
return new PdfReader(getApplicationContext().getResource(getUrl()).getInputStream());
}
protected abstract void mergePdfDocument(Map<String, Object> model, PdfStamper stamper,
HttpServletRequest request, HttpServletResponse response) throws Exception;
}
真正处理业务逻辑的视图解析器PdfStreamingView.java
1 | package com.data2_0.view; |
这里需要说明的是,视图解析器需要指定模板文件url.生成pdf模板文件的过程可以参考其他文章,将模板变量名和对应fields中的key对应着value就可以赋值上了。坑在与我使用的是jetty服务,但是jetty的文件目录在系统的/tmp目录下这个目录下是找不到模板文件的.下面会说道这个问题.
Bean注册
1 | @Bean |
这里要说的是PdfStreamingView,刚说了这里需要指定模板路径。
下载Controller逻辑
DownloadController.java1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68package com.data2_0.controller;
import com.data2_0.annotation.GuestAction;
import com.data2_0.service.ReportRenderService;
import com.data2_0.utils.DateUtil;
import com.data2_0.view.PdfView;
import com.data2_0.view.XlsView;
import com.data2_0.view.XlsxStreamingView;
import com.data2_0.view.XlsxView;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.servlet.ModelAndView;
import java.util.Date;
import java.util.List;
import java.util.Map;
/**
* Created by xiehui1956(@)gmail.com on 17-4-6.
*/
@Controller
@RequestMapping("/api")
public class DownloadController {
@Autowired
private ReportRenderService reportRenderService;
@Autowired
private XlsView xlsView;
@Autowired
private XlsxView xlsxView;
@Autowired
private XlsxStreamingView xlsxStreamingView;
@Autowired
private PdfView pdfView;
/**
* 下载
*
* @param model
*/
@GuestAction
@RequestMapping(value = "/download", method = RequestMethod.GET)
public ModelAndView getDocument(Model model
, @RequestParam Long menuId
, @RequestParam(required = false, defaultValue = "") String type
, @RequestParam @RequestBody String filterParam
, @RequestParam String menuName) throws Exception {
List<String> header = reportRenderService.queryDataFieldNameByConnId(menuId);
List<Map<String, Object>> content = reportRenderService.findAllDataByMenuId(menuId, filterParam);
// 报表名字+日期
model.addAttribute("fileName", String.format("%s-%s", menuName, DateUtil.getDateFormatter(new Date(), "yyyy-MM-dd HH:mm:ss")));
model.addAttribute("header", header);
model.addAttribute("content", content);
switch (type) {
case "xls":
return new ModelAndView(xlsView);
case "xlsx":
return new ModelAndView(xlsxView);
// 超级大的文件用这个
case "xlsxs":
return new ModelAndView(xlsxStreamingView);
case "pdf":
return new ModelAndView(pdfView);
default:
return new ModelAndView(xlsView);
}
}
}
pdf模板控制层ReportController.java
1 | package com.data2_0.controller; |
这里需要注意下文件路径,resource路径设定为模板文件的路径.建议将该配置放到properties文件中管理,方便操作.
以上.