今天要分享輸出PDF格式,跟輸出Excel幾乎相同,最大的差別在於要render PDF麻煩多了,相關的API比較多,雖然iText官網上都有提供教學,不過還是google "iText table"比較快。
在新增class之前需要加入iText的Dependency:
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>itextpdf</artifactId>
<version>5.5.3</version>
</dependency>
到Mvnrepository網站上的最新版是5.5.3,但還有一個舊版及其groupId跟最新版的不同,之後會提到目前Springframework似乎只認得舊版的groupId,等會會看到例外,Springframework提供o.s.web.servlet.view.document.AbstractPdfView供開發者繼承後override buildPdfDocument方法,新增PdfView Class繼承該class,要設定PDF為A4且為橫向,需要再override newDocument方法並設定PageSize參數為A4並呼叫rotate方法,
其code如下
import com.itextpdf.text.Chunk;
import com.itextpdf.text.Document;
import com.itextpdf.text.Element;
import com.itextpdf.text.PageSize;
import com.itextpdf.text.Paragraph;
import com.itextpdf.text.Phrase;
import com.itextpdf.text.pdf.PdfPCell;
import com.itextpdf.text.pdf.PdfPTable;
import com.itextpdf.text.pdf.PdfWriter;
public class PdfView extends AbstractPdfView {
@Autowired
private DCNService dcnService;
@Override
protected Document newDocument() {
// TODO Auto-generated method stub
return new Document(PageSize.A4.rotate());
}
protected void buildPdfDocument(Map<String, Object> model,
Document document, PdfWriter writer, HttpServletRequest request,
HttpServletResponse response) throws Exception {
// TODO Auto-generated method stub
Paragraph p=new Paragraph();//新增一個段落,類似html的<p>
p.add(new Chunk("DCN List")); //新增一個字串然後加到Paragraph
p.setSpacingAfter(30); //設定Space
document.add(p); //每個element都要加到document才會顯示
List<DCN> dcnList=dcnService.findAll();
PdfPTable table=new PdfPTable(DCN.class.getDeclaredFields().length-2); //初始化Table然後指定欄位數目
table.setWidthPercentage(100.0f); //設定Table填滿至頁面Margin //利用Reflect取得DCN的屬性數目然後扣掉
table.setWidths(new int[] {1,2, 1,1,2,1,2});//設定相對欄寬 //Serializable以及MultipartFile兩個屬性
PdfPCell cell = new PdfPCell(); //初始化Cell(欄位)
cell.setPhrase(new Phrase("Serial")); //填入Cell欄位字串,要new一個Phrase
cell.setHorizontalAlignment(Element.ALIGN_CENTER); //置中對齊
table.addCell(cell); //cell加到table中
cell.setPhrase(new Phrase("DCN No"));
cell.setHorizontalAlignment(Element.ALIGN_CENTER);
table.addCell(cell);
cell.setPhrase(new Phrase("DCN Rev"));
cell.setHorizontalAlignment(Element.ALIGN_CENTER);
table.addCell(cell);
cell.setPhrase(new Phrase("Cateogy"));
cell.setHorizontalAlignment(Element.ALIGN_CENTER);
table.addCell(cell);
cell.setPhrase(new Phrase("Tracking Number"));
cell.setHorizontalAlignment(Element.ALIGN_CENTER);
table.addCell(cell);
cell.setPhrase(new Phrase("Issued Date"));
cell.setHorizontalAlignment(Element.ALIGN_CENTER);
table.addCell(cell);
cell.setPhrase(new Phrase("Completed Date"));
cell.setHorizontalAlignment(Element.ALIGN_CENTER);
table.addCell(cell);
Integer recordCounts=1;
for(DCN dcn:dcnList){ //利用for迴圈,把資料填入Cell
table.addCell(recordCounts.toString()); //未支援數值,但可接受字串
table.addCell(dcn.getNo());
table.addCell(dcn.getRev().toString());
table.addCell(dcn.getCategory());
table.addCell(dcn.gettrackNumber().toString());
table.addCell(dcn.getIssuedDate());
table.addCell(dcn.getCompletedDate());
recordCounts++;
}
document.add(table); //table加到document顯示
}
}
接著更新dispatchservletcontext.xml,新增pdfView bean以及加入defaultViews list中
<!-- 新增PDF View bean並指定對應的class -->
<bean id="pdfView" class=" tw.blogger.springtech.springmvc.view.PdfView"/>
<bean
class="org.springframework.web.servlet.view.ContentNegotiatingViewResolver">
<property name="defaultViews">
<list>
<ref bean="jsonView" />
<ref bean="xmlView" />
<ref bean="excelView"/>
<ref bean="pdfView"/>
</list>
</property>
</bean>
啟動Server,存取DCNList.jsp時會產生Exception,畫面如下。
因為iText新版的套件的名稱已經更換,AbstractPdfView僅認得com.lowagie套件的iText,故須用舊版的dependency:
<dependency>
<groupId>com.lowagie</groupId>
<artifactId>itext</artifactId>
<version>4.2.1</version>
</dependency>
回到PdfView重新import相關套件,其實新版跟舊版的iText基本組件名稱並未改變,只是套件名稱不同而已。
並重新啟動Server,DCN List清單畫面如下:
輸出PDF檔以及Console log如下
23:18:14 [http-nio-8080-exec-36] DispatcherServlet - DispatcherServlet with name 'dispatcher' processing GET request for [/SpringMVC/dcn.pdf]
23:18:14 [http-nio-8080-exec-36] RequestMappingHandlerMapping - Looking up handler method for path /dcn.pdf
23:18:14 [http-nio-8080-exec-36] RequestMappingHandlerMapping - Returning handler method [public java.lang.String tw.blogger.springtech.springmvc.controller.DCNController.DCNList(org.springframework.ui.Model)]
23:18:14 [http-nio-8080-exec-36] DispatcherServlet - Last-Modified value for [/SpringMVC/dcn.pdf] is: -1
Hibernate: select dcn0_.prikey as prikey1_0_, dcn0_.category as category2_0_, dcn0_.completedDate as complete3_0_, dcn0_.issuedDate as issuedDa4_0_, dcn0_.no as no5_0_, dcn0_.rev as rev6_0_, dcn0_.trackNumber as trackNum7_0_ from DCN dcn0_
23:18:14 [http-nio-8080-exec-36] ContentNegotiatingViewResolver - Requested media types are [application/pdf] based on Accept header types and producible media types [*/*])
23:18:14 [http-nio-8080-exec-36] ContentNegotiatingViewResolver - Returning [tw.blogger.springtech.springmvc.view.PdfView: name 'pdfView'] based on requested media type 'application/pdf'
23:18:14 [http-nio-8080-exec-36] DispatcherServlet - Rendering view [tw.blogger.springtech.springmvc.view.PdfView: name 'pdfView'] in DispatcherServlet with name 'dispatcher'
Hibernate: select dcn0_.prikey as prikey1_0_, dcn0_.category as category2_0_, dcn0_.completedDate as complete3_0_, dcn0_.issuedDate as issuedDa4_0_, dcn0_.no as no5_0_, dcn0_.rev as rev6_0_, dcn0_.trackNumber as trackNum7_0_ from DCN dcn0_
23:18:15 [http-nio-8080-exec-36] DispatcherServlet - Successfully completed request
明日預計開始分享Spring Security或是穿插Hibernate Validator。