基於freemarker實現excel的模板匯出
這裡簡單介紹一下使用freemarker的好處,適用於複雜的模板匯出功能,並且還可以保持原有模板的一些方法。
第一步:新增依賴
我的專案是基於maven,所以在pom.xml中新增一下依賴就可以了:
<dependency>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
<version>2.3.20</version>
</dependency>
第二步:把excel另存為xml
在這裡我要說一下,excel在另存為xml時裡面的圖片會丟失,它並不會像word那樣生成一個佔位符。所以我這個匯出的excel是沒有圖片的。言歸正傳,這就是我另存為的xml的樣例,其中row為行,cell為單元格
第三步:把要插入的資料使用freemarker的標籤----${}替換
看到這裡的${},是不是覺得它很像jstl標籤,它的用法確實和jstl差不多,jstl是獲取session中的值,而他是獲取dataMap中的值;而且都是可以迴圈的便利的,它的便利標籤就是
<#list list as item><#--其中list為你後臺在dataMap中的key-->
<#if item.detaildata??><#--判斷是否為空,如果不為空則顯示內容-->
<#--${item.detaildata}是獲取list的子元素的值-->
<Cell ss:StyleID="s37"><Data ss:Type="String">${item.detaildata}</Data></Cell>
<#else><#--否則顯示空的單元格-->
<Cell ss:StyleID="s37"/>
</#if>
</#list>
我想很多人還是不清楚我講的,可能確實是我的語言組織能力不好,不多說直接上截圖,簡潔明瞭
第四步:把xml的字尾名改成ftl,並且在webapp中新建template,把xxx.ftl,放到裡面
好了基本的操作已經做完了,下面就是後臺程式碼了。因為我的專案是MVC的設計理念並沒有使用任何框架,所以把後臺分為了四個層面:資料庫物件序列化(dto),資料庫訪問層(DAO),資料處理層(service),控制層(servlet),算了還是用一個簡單的例子吧,我開發的專案可能就要從頭講了。廢話不多說先上service的程式碼,是一個封裝好的類,可以直接呼叫的:
public class ExportExcelService {
public static void export(String templatePath, Map<String, Object> dataMap,
String buildFile, String newName,HttpServletRequest request,HttpServletResponse response) {
try {
ServletContext application = request.getSession().getServletContext();
Configuration configuration = new Configuration();
configuration.setDefaultEncoding("utf-8");
String path = application.getRealPath("template");
// 此處是本類Class.getResource()相對於模版檔案的相對路徑
configuration.setDirectoryForTemplateLoading(new File(path));
Template template = null;
File outFile = new File(buildFile);
Writer writer = null;
template = configuration.getTemplate(templatePath);
template.setEncoding("utf-8");
writer = new BufferedWriter(new OutputStreamWriter(
new FileOutputStream(outFile), Charset.forName("utf-8")));// 此處為輸
template.process(dataMap, writer);
writer.flush();
writer.close();
// return true;
// 設定response的編碼方式
response.setContentType("application/x-msdownload");
// 設定附加檔名
response.setHeader("Content-Disposition", "attachment;filename="
+ new String(newName.getBytes("utf-8"), "iso-8859-1"));
// 讀出檔案到i/o流
FileInputStream fis = new FileInputStream(outFile);
BufferedInputStream buf = new BufferedInputStream(fis);
byte[] b = new byte[1024];// 相當於我們的快取
long k = 0;// 該值用於計算當前實際下載了多少位元組
// 從response物件中得到輸出流,準備下載
OutputStream myout = response.getOutputStream();
// 開始迴圈下載
while (k < outFile.length()) {
int j = buf.read(b, 0, 1024);
k += j;
// 將b中的資料寫到客戶端的記憶體
myout.write(b, 0, j);
}
// 將寫入到客戶端的記憶體的資料,重新整理到磁碟
myout.flush();
myout.close();
} catch (Exception e) {
e.printStackTrace();
// return false;
}
}
}
然後我們在servlet中呼叫這個類中的方法,重點就看我標黃的程式碼,這個是自己生成的list,而不是通過其他程式碼生成的,標藍色的則是單一的鍵值對,紅色程式碼為如果匯出的資料不是模板的7天資料,則自動補充不足的空資料,以完成模板顯示空單元格如下:
@WebServlet("/excelExport")
public class exportExcelServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String uz=new String(URLDecoder.decode(req.getParameter("uz"),"utf-8"));
ReimburseService res=new ReimburseService();
String[] data=uz.split(",");
String uname=uz.split(",")[1];
String weeknumber=uz.split(",")[0];
Model model=res.querydetail(uname, weeknumber);
Map<String, Object> dataMap = new HashMap<String, Object>();
dataMap.put("username",uz.split(",")[2]);
dataMap.put("writedata",uz.split(",")[3]);
dataMap.put("appartment",uz.split(",")[4]);
List list=(List) model.getData();
for(int i=list.size();i<7;i++){
ReimburseDetail rd=new ReimburseDetail();
rd.setDetaildata("");
rd.setHotelfare("");
rd.setLocation("");
rd.setLongfare("");
rd.setOtherfare("");
rd.setShortfare("");
rd.setProject("");
rd.setFaredescription("");
list.add(rd);
}
//對list進行排序
for(int i=0;i<list.size();i++){
ReimburseDetail rdl=(ReimburseDetail)list.get(i);
try {
if(!rdl.getDetaildata().equals("")){
int b=dayForWeek(rdl.getDetaildata());
if(b-1!=i){
Object objA= list.get(i);
list.set(i, list.get(b-1));
list.set(b-1, objA);
}
}
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
dataMap.put("list",list);
List ls=new ArrayList();
String[] num={"A","B","C","D","E","F","G"};
String[] Formula={"-9","-10","-11","-12","-13","-14","-15"};
for(int i=0;i<num.length;i++){
Map<String,Object> map = new HashMap<String,Object>();
map.put("num",num[i]);
ReimburseDetail r=(ReimburseDetail)list.get(i);
map.put("faredescription",r.getFaredescription());
map.put("Formula", Formula[i]);
map.put("col", i+1);
ls.add(map);
}
dataMap.put("ls",ls);
ServletContext application = req.getSession().getServletContext();
/*********輸出圖片未解決********************/
String a=application.getRealPath("imgs");
String img = getImageStr(a+"/ceit.png");
dataMap.put("image", img);
/***********************************/
String templateName = "excel.ftl"; //模板名稱
java.text.DateFormat format1 = new java.text.SimpleDateFormat("yyyy-MM-dd");
String exportexcel = application.getRealPath("template")+format1.format(new Date())+ ".xls";
ExportExcelService.export(templateName, dataMap, exportexcel, "每週報銷單("+format1.format(new Date())+").xls",req,resp);
}
/***************獲取日期是星期幾*************************/
public static int dayForWeek(String pTime) throws Exception {
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd" );
Calendar c = Calendar.getInstance();
c.setTime(format.parse(pTime));
int dayForWeek = 0 ;
if (c.get(Calendar.DAY_OF_WEEK) == 1 ){
dayForWeek = 7 ;
}else {
dayForWeek = c.get(Calendar.DAY_OF_WEEK) - 1 ;
}
return dayForWeek;
}
/**************將圖片轉成base64********************/
public String getImageStr(String imgFile) {
InputStream in = null;
String da="";
byte[] data = null;
try {
in = new FileInputStream(imgFile);
data = new byte[in.available()];
in.read(data);
in.close();
da=new String(Base64.encodeBase64(data),"UTF-8");
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return da;
}
}
好了,文章寫的這裡就圓滿結束了。唯一的遺憾就是圖片匯出還沒有解決,等解決了再來補充,當然如果是匯出word模板是可以匯出圖片的因為再把帶有圖片的word轉xml時會生成這個標籤------》<w:binDataw: name="wordml://03000001.png" xml:space ="preserve">${image}</w:binData><v:shape id="_x0000_i1025" type="#_x0000_t75" style="width:414.75pt; height:319.5pt"> <v:imagedata src="wordml://03000001.png" o:title=""/></v:shape>關於這個網上有很多,可以多去逛逛。