POI详细教程
http://wenku.baidu.com/view/089285d726fff705cc170ab3.html
?
第一章???? POI簡(jiǎn)介
--Jakata?Poi?HSSF:純java的Excel解決方案
在我們實(shí)際的開發(fā)中,表現(xiàn)層的解決方案雖然有多樣,但是IE瀏覽器已成為最多人使用的瀏覽器,因?yàn)榇蠹叶加?span style="font-family:Times New Roman">Windows。在企業(yè)辦公系統(tǒng)中,常常有客戶這樣子要求:你要把我們的報(bào)表直接用Excel打開(電信系統(tǒng)、銀行系統(tǒng))。或者是:我們已經(jīng)習(xí)慣用Excel打印。
Apache的Jakata項(xiàng)目的POI子項(xiàng)目,目前比較成熟的是HSSF接口,處理MSExcel對(duì)象。它不象我們僅僅是用csv生成的沒有格式的可以由Excel轉(zhuǎn)換的東西,而是真正的Excel對(duì)象,你可以控制一些屬性如sheet,cell等等。
首先,理解一下一個(gè)Excel的文件的組織形式,一個(gè)Excel文件對(duì)應(yīng)于一個(gè)workbook(HSSFWorkbook),一個(gè)workbook可以有多個(gè)sheet(HSSFSheet)組成,一個(gè)sheet是由多個(gè)row(HSSFRow)組成,一個(gè)row是由多個(gè)cell(HSSFCell)組成。
POI可以到www.apache.org下載到。實(shí)際運(yùn)行時(shí),需要有poi包就可以了。HSSF提供給用戶使用的對(duì)象在rg.apache.poi.hssf.usermodel包中,主要部分包括Excel對(duì)象,樣式和格式,還有輔助操作。有以下幾種對(duì)象:
HSSFWorkbook ???????????????????????????????????????????????????????????????? excel的文檔對(duì)象
HSSFSheet ??????????????????????????????????????????????????????????????????????????????????? excel的表單
HSSFRow ???????????????????????????????????????????????????????????????????????? excel的行
HSSFCell ????????????????????????????????????????????????????????????????????????? excel的格子單元
HSSFFont ???????????????????????????????????????????????????????????????????????? excel字體
HSSFDataFormat ?????????????????????????????????????????????????????????????? 日期格式
在poi1.7中才有以下2項(xiàng):
HSSFHeader ????????????????????????????????????????????????????????????????????????????????? sheet頭
HSSFFooter ????????????????????????????????????????????????????????????????????????????????? sheet尾(只有打印的時(shí)候才能看到效果)
和這個(gè)樣式
HSSFCellStyle ?????????????????????????????????????????????????????????????????? cell樣式
輔助操作包括
HSSFDateUtil ??????????????????????????????????????????????????????????????????? 日期
HSSFPrintSetup ???????????????????????????????????????????????????????????????? 打印
HSSFErrorConstants ????????????????????????????????????????????????????????? 錯(cuò)誤信息表
以下可能需要使用到如下的類
import org.apache.poi.hssf.usermodel.HSSFCell;
import org.apache.poi.hssf.usermodel.HSSFCellStyle;
import org.apache.poi.hssf.usermodel.HSSFDataFormat;
import org.apache.poi.hssf.usermodel.HSSFFont;
import org.apache.poi.hssf.usermodel.HSSFRow;
import org.apache.poi.hssf.usermodel.HSSFSheet;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.hssf.util.HSSFColor;
?
先看poi的examples包中提供的最簡(jiǎn)單的例子,建立一個(gè)空xls文件。
| import java.io.FileOutputStream; import java.io.IOException; import org.apache.poi.hssf.usermodel.HSSFWorkbook; public class ExcelSample1 { ? ??????????? public static void main(String[] args) throws IOException { ??????????????????????? //創(chuàng)建一個(gè)excel文件 ??????????????????????? HSSFWorkbook wb= new HSSFWorkbook(); ??????????????????????? FileOutputStream fileOut= new FileOutputStream("c:\\workbook.xls"); // FileOutputStream fileOut= new FileOutputStream("c:/workbook.xls"); ??????????????????????? wb.write(fileOut); ??????????????????????? fileOut.close(); ??????????? } } |
通過這個(gè)例子,我們?cè)?span style="font-family:Times New Roman">c盤下建立的是一個(gè)空白的xls文件(不是空文件)。在此基礎(chǔ)上,我們可以進(jìn)一步看其它的例子。
| import org.apache.poi.hssf.usermodel.*; import java.io.FileOutputStream; import java.io.IOException; public class CreateCells { public static void main(String[] args) throws IOException { HSSFWorkbook wb = new HSSFWorkbook();??? //建立新HSSFWorkbook對(duì)象 HSSFSheet sheet = wb.createSheet("new sheet");? //建立新的sheet對(duì)象 HSSFRow row = sheet.createRow((short)0); //在sheet里創(chuàng)建一行,參數(shù)為行號(hào)(第一行,此處可想象成數(shù)組) HSSFCell cell = row.createCell((short)0);????? //在row里建立新cell(單元格),參數(shù)為列號(hào)(第一列) cell.setCellvalue(1); ??????????????????????//設(shè)置cell的整數(shù)類型的值 row.createCell((short)1).setCellvalue(1.2);???? //設(shè)置cell浮點(diǎn)類型的值 row.createCell((short)2).setCellvalue("test");?? //設(shè)置cell字符類型的值 row.createCell((short)3).setCellvalue(true);??? //設(shè)置cell布爾類型的值 HSSFCellStyle cellStyle = wb.createCellStyle(); //建立新的cell樣式 cellStyle.setDataFormat(HSSFDataFormat. getBuiltinFormat("m/d/yy h:mm")); //設(shè)置cell樣式為定制的日期格式 HSSFCell dCell =row.createCell((short)4); dCell.setCellvalue(new Date());??????????? //設(shè)置cell為日期類型的值 dCell.setCellStyle(cellStyle); ?????????????//設(shè)置該cell日期的顯示格式 HSSFCell csCell =row.createCell((short)5); csCell.setEncoding(HSSFCell.ENCODING_UTF_16); //設(shè)置cell編碼解決中文高位字節(jié)截?cái)?/p> csCell.setCellvalue("中文測(cè)試_Chinese Words Test");? //設(shè)置中西文結(jié)合字符串 row.createCell((short)6).setCellType(HSSFCell.CELL_TYPE_ERROR); //建立錯(cuò)誤cell FileOutputStream fileOut = new FileOutputStream("workbook.xls"); wb.write(fileOut); fileOut.close(); } } |
通過這個(gè)例子,我們可以清楚的看到xls文件從大到小包括了HSSFWorkbook HSSFSheet HSSFRow HSSFCell這樣幾個(gè)對(duì)象。我們可以在cell中設(shè)置各種類型的值。
?? 尤其要注意的是如果你想正確的顯示非歐美的字符時(shí),尤其象中日韓這樣的語言,必須設(shè)置編碼為16位的即是HSSFCell.ENCODING_UTF_16,才能保證字符的高8位不被截?cái)喽鹁幋a失真形成亂碼。
?? 其他測(cè)試可以通過參考examples包中的測(cè)試?yán)诱莆?span style="font-family:Times New Roman">poi的詳細(xì)用法,包括字體的設(shè)置,cell大小和低紋的設(shè)置等。需要注意的是POI是一個(gè)仍然在完善中的公開代碼的項(xiàng)目,所以有些功能正在不斷的擴(kuò)充。
?????? 感覺上面的操作比較的繁瑣,然后就自己寫了一個(gè)方法。這個(gè)方法不需要事先創(chuàng)建row和cell,直接進(jìn)行cteateCell就可以了,在程序中會(huì)自動(dòng)進(jìn)行判斷,如果不存在的話會(huì)創(chuàng)建。
| ?????? private static void? cteateCell(HSSFWorkbook wb,HSSFRow row,short col,short align,String val){ ?????????????????? HSSFCell cell = row.createCell(col); ?????????????????? cell.setEncoding(HSSFCell.ENCODING_UTF_16); ?????????????????? cell.setCellValue(val); ?????????????????? HSSFCellStyle cellstyle = wb.createCellStyle(); ?????????????????? cellstyle.setAlignment(align); ?????????????????? cell.setCellStyle(cellstyle); ?????? } |
對(duì)里面的幾個(gè)參數(shù)的說明:
short col?????????? 應(yīng)該是你的cell單元格的位置也就是列號(hào);
short align???????? 應(yīng)該是你的對(duì)齊方式;
String val????????? 應(yīng)該是你單元格里面要添加的值;
具體的調(diào)用如下:
| HSSFRow row = sheet.createRow((short)1); cteateCell(wb,row,(short)0,HSSFCellStyle.ALIGN_CENTER_SELECTION,"SampleID"); |
在上邊的例子里我們看到了要設(shè)置一個(gè)單元格里面信息的格式(例如,要將信息居中)設(shè)置的操作如下:
| HSSFCellStyle cellstyle = wb.createCellStyle(); cellstyle.setAlignment(HSSFCellStyle.ALIGN_CENTER_SELECTION); cell.setCellStyle(cellstyle); |
還有我們我們經(jīng)常會(huì)用到的合并單元格,在這里我們也有這樣的操作,代碼如下:
| sheet.addMergedRegion(new Region(1,(short)1,2,(short)4)); |
?
這里面我們還要介紹一個(gè)經(jīng)常會(huì)遇到的問題,就是怎么來凍結(jié)一個(gè)窗口。poi也為我們集成了這樣的事情了。代碼如下:
| ??????????? sheet.createFreezePane(1,2); |
l? 在這里我們需要注意的是
一、該方法是在一個(gè)具體的sheet里面來進(jìn)行操作。
二、方法createFreezepane;有2個(gè)參數(shù)。前一個(gè)參數(shù)代表列;后一個(gè)參數(shù)代表行。
上邊的代碼對(duì)應(yīng)的excel文件如下:
我么在畫面上看到了明顯的兩條黑線,這就是凍結(jié)的窗口。
然后我們來看一個(gè)完整的STRUTS的小例子,在這個(gè)例子里面我們要做的事情是要模擬移動(dòng)公司的網(wǎng)上營業(yè)廳里面的一個(gè)功能,我們要把一個(gè)客戶當(dāng)月的通話記錄和各種信息查詢出來,并且生成一張excel報(bào)表。首先,我們來看一下網(wǎng)上效果的截圖。
然后就是我們具體的代碼實(shí)現(xiàn)了。
struts-config.xml
| <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE struts-config PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 1.2//EN" "http://struts.apache.org/dtds/struts-config_1_2.dtd"> <struts-config> ??????????? <action-mappings> ??????????? <action ??????????????????????? path="/search" ????????????? ??????????????????????? type="action.SearchAction"> ??????????????????????????????????? <forward name="success" path="/detial.jsp"/> ??????????? </action> ??????????? <action ??????????????????????? path="/down" ??????????????????????? type="action.DownAction"> ??????????????????????????????????? ?<forward name="display" path="/down.jsp" /> ??????????? </action> ??????????? </action-mappings> ??????????? <message-resources parameter="ApplicationResources" /> </struts-config> |
?
index.jsp
| <%@ page contentType="text/html; charset=gb2312" language="java"%> <html> ? <head>??? ??? <title>歡迎進(jìn)入POI-Excel文件報(bào)表系統(tǒng)</title>??????? ? </head> ? <body> <table align="center"> ??????????? <tr> ??????????????????????? <td align="center">歡迎進(jìn)入POI-Excel文件報(bào)表系統(tǒng)</td> ??????????? </tr> ??????????????????????? <tr> ??????????????????????? <td align="center"> <a href="<%=request.getContextPath()%>/search.do">進(jìn)入查詢頁面</a> </td> ??????????? </tr> </table> </body> </html> |
連接數(shù)據(jù)庫的SQLBean,這個(gè)bean和我們之前在分頁里面用到的bean是一樣的。
| package bean; import java.sql.Connection; import java.sql.DriverManager; import java.sql.ResultSet; import java.sql.Statement; public class SQLBean { ??? String url="jdbc:microsoft:sqlserver://localhost:1433;DatabaseName=poi_mvc"; ??? Connection con=null; ??? Statement sta=null; ??? public SQLBean() ??? { ??????? try ??????? { ??????????? Class.forName("com.microsoft.jdbc.sqlserver.SQLServerDriver"); ??????????? con=DriverManager.getConnection(url,"sa",""); ??????????? sta=con.createStatement(); ??????? } ??????? catch(Exception e) ??????? { ??????????? e.printStackTrace(); ??????? } ??? } ??? public ResultSet select(String selects) throws Exception ??????????? ??? { ??????????? ??????? return sta.executeQuery(selects); ??????????? ??? } } |
為了體現(xiàn)MVC模式,我們?cè)谶@里面引入了一個(gè)存儲(chǔ)數(shù)據(jù)的Bean。EntityBean.java
| package bean; ? public class EntityBean { ??????????? private String sjhm; ?????? private String hjlx; ??????? private String dfhm; ??????????? private String qssj; ??????? private String thsc; ??????? private String thdd; ??????????? private String ctlx;? ?????? private String jbhf; ??????? private String chf; ??????????? private String zhf;? ??????? private String zjls; ??????????????????????? ??????????? public EntityBean(String sjhm,String hjlx,String dfhm,String qssj, ??????????????????????????????????? ?????????? String thsc,String thdd,String ctlx,String jbhf, ??????????????????????????????????? ?????????? String chf,String zhf){ ??????????????????????? this.sjhm=sjhm;????????????? this.hjlx=hjlx;??????????????? this.dfhm=dfhm; ??????????????????????? this.qssj=qssj;???????????????? this.thsc=thsc;??????????????? this.thdd=thdd; ??????????????????????? this.ctlx=ctlx;???????????????? this.jbhf=jbhf;??????????????? this.chf=chf; ??????????????????????? this.zhf=zhf;????? } ??????????? public String getChf() { ??????????????????????? return chf; ??????????? } ??????????? public void setChf(String chf) { ??????????????????????? this.chf = chf; ??????????? } ??????????? public String getCtlx() { ??????????????????????? return ctlx; ??????????? } ??????????? public void setCtlx(String ctlx) { ??????????????????????? this.ctlx = ctlx; ??????????? } ??????????? public String getDfhm() { ??????????????????????? return dfhm; ??????????? } ??????????? public void setDfhm(String dfhm) { ??????????????????????? this.dfhm = dfhm; ??????????? } ??????????? public String getHjlx() { ??????????????????????? return hjlx; ??????????? } ??????????? public void setHjlx(String hjlx) { ??????????????????????? this.hjlx = hjlx; ??????????? } ??????????? public String getJbhf() { ??????????????????????? return jbhf; ??????????? } ??????????? public void setJbhf(String jbhf) { ??????????????????????? this.jbhf = jbhf; ??????????? } ??????????? public String getQssj() { ??????????????????????? return qssj; ??????????? } ??????????? public void setQssj(String qssj) { ??????????????????????? this.qssj = qssj; ??????????? } ??????????? public String getSjhm() { ??????????????????????? return sjhm; ??????????? } ??????????? public void setSjhm(String sjhm) { ??????????????????????? this.sjhm = sjhm; ??????????? } ??????????? public String getThdd() { ??????????? ??????????? return thdd; ??????????? } ??????????? public void setThdd(String thdd) { ??????????????????????? this.thdd = thdd; ??????????? } ??????????? public String getThsc() { ??????????????????????? return thsc; ??????????? } ??????????? public void setThsc(String thsc) { ??????????????????????? this.thsc = thsc; ??????????? } ??????????? public String getZhf() { ??????????????????????? return zhf; ??????????? } ??????????? public void setZhf(String zhf) { ??????????????????????? this.zhf = zhf; ??????????? } ??????????? public String getZjls() { ??????????????????????? return zjls; ??????????? } ??????????? public void setZjls(String zjls) { ??????????????????????? this.zjls = zjls; ??????????? }????????? } |
然后讓我們來看看SearchAction,他的主要功能把數(shù)據(jù)庫里的數(shù)據(jù)提取出來,然后封裝到EntityBean里面,在轉(zhuǎn)發(fā)到一個(gè)新的頁面。
| package action; ? import java.sql.ResultSet; import java.util.ArrayList; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.struts.action.Action; import org.apache.struts.action.ActionForm; import org.apache.struts.action.ActionForward; import org.apache.struts.action.ActionMapping; import bean.EntityBean; import bean.SQLBean; ? public class SearchAction extends Action { ??????????? public Log log = LogFactory.getLog("Poi_mvc"); ??????????? public ActionForward execute( ??????????????????????? ActionMapping mapping, ??????????????????????? ActionForm form, ??????????????????????? HttpServletRequest request, ??????????????????????? HttpServletResponse response) { ??????????????????????? ArrayList list = new ArrayList(); ??????????????????????? SQLBean sq = new SQLBean(); ??????????????????????? String sql = "select * from detial"; ??????????????????????? try{ ??????????????????????????????????? ResultSet res = sq.select(sql); ??????????????????????????????????? while(res.next()){ ??????????????????????????????????????????????? String sjhm = res.getString("sjhm"); ??????????????????????????????????????????????? String hjlx = res.getString("hjlx"); ??????????????????????????????????????????????? String dfhm = res.getString("dfhm"); ??????????????????????????????????????????????? String qssj = res.getString("qssj"); ??????????????????????????????????????????????? String thsc = res.getString("thsc"); ??????????????????????? ??????????????????????? String thdd = res.getString("thdd"); ??????????????????????????????????????????????? String ctlx = res.getString("ctlx"); ??????????????????????????????????????????????? String jbhf = res.getString("jbhf"); ??????????????????????????????????????????????? String chf = res.getString("chf"); ??????????????????????????????????????????????? String zhf = res.getString("zhf"); ??????????????????????????????????????????????? log.info("開始封裝數(shù)據(jù):手機(jī)號(hào)碼="+sjhm+"呼叫類型" +hjlx+"對(duì)方號(hào)碼"+dfhm+"起始時(shí)間"+qssj+"通話時(shí)間" +thsc+"通話地點(diǎn)"+thdd+"長途類型"+ctlx+"基本話費(fèi)" +jbhf+"常話費(fèi)"+chf+"總話費(fèi)"+zhf+"總紀(jì)錄"+i); ??????????????????????? EntityBean eb = new EntityBean(sjhm,hjlx,dfhm,qssj,thsc,thdd,ctlx,jbhf,chf,zhf); ??????????????????????????????????????????????? list.add(eb); ??????????????????????????????????????????????? } ??????????????????????????????????? res.close(); ??????????????????????? } ??????????????????????? catch(Exception e){ ??????????????????????????????????? e.printStackTrace(); ??????????????????????? }????????????????????? ??????????????????????? request.setAttribute("result",list); ??????????????????????? return mapping.findForward("success"); ??????????? } } |
顯示頁面detail.jsp
| <%@ page contenttype="text/html; charset=gb2312" language="java"%> ? <%@ taglib uri="http://jakarta.apache.org/struts/tags-bean" ?????????? prefix="bean"%> <%@ taglib uri="http://jakarta.apache.org/struts/tags-html" ?????????? prefix="html"%> <%@ taglib uri="http://jakarta.apache.org/struts/tags-logic" ????????? prefix="logic"%> <html> <head> <title>顯示查詢結(jié)果</title> </head> <body> <div align="center"> <table border="1" align="center"> ??????????? <tr> ??????????????????????? <td colspan="10" align="center"><html:link page="/down.do">點(diǎn)擊生成excel文件</html:link></td> ??????????? </tr> ? ??????????? <tr> ??????????????????????? <td colspan="10" align="center">總紀(jì)錄數(shù):<bean:size id="zj" name="result"/><bean:write name="zj" /></td> ??????????? </tr> ??????????? <tr> ??????????????????????? <td> ??????????????????????? <p align="center">手機(jī)號(hào)碼</p> ??????????????????????? </td> ??????????????????????? <td> ??????????????????????? <p align="center">呼叫類型</p> ??????????????????????? </td> ??????????????????????? <td> ??????????????????????? <p align="center">對(duì)方號(hào)碼</p> ??????????????????????? </td> ??????????????????????? <td> ??????????????????????? <p align="center">起始時(shí)間</p> ??????????????????????? </td> ??????????????????????? <td> ??????????????????????? <p align="center">通話時(shí)間</p> ??????????????????????? </td> ??????????????????????? <td> ??????????????????????? <p align="center">通話地點(diǎn)</p> ??????????????????????? </td> ??????????????????????? <td> ??????????????????????? <p align="center">長途類型</p> ??????????????????????? </td> ??????????????????????? <td> ??????????????????????? <p align="center">基本話費(fèi)</p> ??????????????????????? </td> ??????????????????????? <td> ??????????????????????? <p align="center">長話費(fèi)</p> ??????????????????????? </td> ??????????????????????? <td> ??????????????????????? <p align="center">總話費(fèi)</p> ??????????????????????? </td> ??????????? </tr> ? ??????????? <logic:iterate id="res" name="result"> ??????????????????????? <tr> ??????????????????????????????????? <td> ??????????????????????????????????? <div align="center"><bean:write name="res" property="sjhm" /></div> ??????????????????????????????????? </td> ??????????????????????????????????? <td> ??????????????????????????????????? <div align="center"><bean:write name="res" property="hjlx" /></div> ??????????????????????????????????? </td> ??????????????????????????????????? <td> ??????????????????????????????????? <div align="center"><bean:write name="res" property="dfhm" /></div> ??????????????????????????????????? </td> ??????????????????????????????????? <td> ??????????????????????????????????? <div align="center"><bean:write name="res" property="qssj" /></div> ??????????????????????????????????? </td> ??????????????????????????????????? <td> ??????????????????????????????????? <div align="center"><bean:write name="res" property="thsc" /></div> ??????????????????????????????????? </td> ??????????????????????????????????? <td> ??????????????????????????????????? <div align="center"><bean:write name="res" property="thdd" /></div> ??????????????????????????????????? </td> ??????????????????????????????????? <td> ??????????????????????????????????? <div align="center"><bean:write name="res" property="ctlx" /></div> ??????????????????????????????????? </td> ??????????????????????????????????? <td> ??????????????????????????????????? <div align="center"><bean:write name="res" property="jbhf" /></div> ??????????????????????????????????? </td> ??????????????????????????????????? <td> ??????????????????????????????????? <div align="center"><bean:write name="res" property="chf" /></div> ??????????????????????????????????? </td> ??????????????????????????????????? <td> ??????????????????????????????????? <div align="center"><bean:write name="res" property="zhf" /></div> ??????????????????????????????????? </td> ??????????????????????? </tr> ??????????? </logic:iterate> ??????????? <tr> ??????????????????????? <td colspan="10">總紀(jì)錄數(shù):<bean:write name="zj" /></td> ??????????? </tr> </table> </div> </body> </html> |
?
模擬效果圖:
當(dāng)我們點(diǎn)擊生成excel文件這個(gè)超鏈接的時(shí)候,會(huì)找到這個(gè)DownAction
| package action; ? import java.io.OutputStream; import java.sql.ResultSet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.struts.action.Action; import org.apache.struts.action.ActionForm; import org.apache.struts.action.ActionForward; import org.apache.struts.action.ActionMapping; import bean.ExcelBean; import bean.SQLBean; ? public class DownAction extends Action { ??????????? public ActionForward execute(ActionMapping mapping, ActionForm form, ??????????????????????????????????? HttpServletRequest request, HttpServletResponse response) ??????????????????????????????????? throws Exception { ??????????????????????? SQLBean sq = new SQLBean(); ??????????????????????? String sql = "select * from detial"; ??????????????????????? try { ??????????????????????????????????? String fname = "detial";// Excel文件名 ??????????? ??????????????????????? OutputStream os = response.getOutputStream();// 取得輸出流 ??????????????????????????????????? response.reset();// 清空輸出流 ??????????????????????????????????? response.setHeader("Content-disposition", "attachment; filename=" ??????????????????????????????????????????????????????????? + fname + ".xls"); // 設(shè)定輸出文件頭,該方法有兩個(gè)參數(shù),分別表示應(yīng)答頭的名字和值。 ? ??????????????????????????????????? response.setContentType("application/msexcel"); // 定義輸出類型 ? ??????????????????????????????????? ResultSet res = sq.select(sql);?????????????????????????????? ??????????????????????????????????? ExcelBean eb = new ExcelBean(); ??????????????????????????????????? eb.createFixationSheet(res, os);// 調(diào)用生成excel文件bean ??????????????????????????????????? res.close(); ??????????????????????? } catch (Exception e) { ??????????????????????????????????? System.out.println(e); ??????????????????????? } ? ??????????????????????? return mapping.findForward("display"); ??????????? } ? } |
在這個(gè)action里面我們調(diào)用了ExcelBean
| package bean; ? import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStream; import java.sql.ResultSet; import java.sql.SQLException; import org.apache.poi.hssf.usermodel.HSSFCell; import org.apache.poi.hssf.usermodel.HSSFCellStyle; import org.apache.poi.hssf.usermodel.HSSFRow; import org.apache.poi.hssf.usermodel.HSSFSheet; import org.apache.poi.hssf.usermodel.HSSFWorkbook; import org.apache.poi.hssf.util.Region; public class ExcelBean { ??? private HSSFWorkbook wb=null; ??? public ExcelBean() ??? { ??????? wb=new HSSFWorkbook(); ??? }?? ??? public void createFixationSheet(ResultSet res,OutputStream os) throws IOException ??? { ??????? HSSFSheet sheet=wb.createSheet("new sheet"); ??????? wb.setSheetName(0,"話費(fèi)詳單",HSSFWorkbook.ENCODING_UTF_16); ??????? HSSFRow row=sheet.createRow((short)0); ??????? sheet.createFreezePane(0,1); ??????? cteateCell(wb,row,(short)0,"手機(jī)號(hào)碼"); ??????? cteateCell(wb,row,(short)1,"呼叫類型"); ??????? cteateCell(wb,row,(short)2,"對(duì)方號(hào)碼"); ??????? cteateCell(wb,row,(short)3,"起始時(shí)間"); ??????? cteateCell(wb,row,(short)4,"通話時(shí)間"); ??????? cteateCell(wb,row,(short)5,"通話地點(diǎn)"); ??????? cteateCell(wb,row,(short)6,"長途類型"); ??????? cteateCell(wb,row,(short)7,"基本話費(fèi)"); ??????? cteateCell(wb,row,(short)8,"長話費(fèi)"); ??????? cteateCell(wb,row,(short)9,"總話費(fèi)");??????? ??????? int ii=0; ??????? try ??????? { ??????????? int i=0; ??????????? ii=res.getMetaData().getColumnCount(); ??????????? while(res.next()) ??????????? { ??????????????? i++; ??????????????? HSSFRow row2=sheet.createRow((short)i); ??????????????? for(int j=0;j<ii;j++) ??????????????? { ??????????????????? String ss=""; ??????????????????? if(res.getString(j+1)==null) ??????????????????????? ss="空? null"; ??????????????????? else ??????????????????????? ss=res.getString(j+1); ??????????????????? cteateCell(wb,row2,(short)j,ss); ??????????????? } ??????????? } ??????? } catch(SQLException e) ??????? { ??????????? e.printStackTrace(); ??????? } ??????? wb.write(os); ????? os.flush(); ????? os.close(); ??? } ??? private void cteateCell(HSSFWorkbook wb,HSSFRow row,short col,String val) ??? { ??????? HSSFCell cell=row.createCell(col); ??????? cell.setEncoding(HSSFCell.ENCODING_UTF_16); ??????? cell.setCellValue(val); ??????? HSSFCellStyle cellstyle=wb.createCellStyle(); ??????? cellstyle.setAlignment(HSSFCellStyle.ALIGN_CENTER_SELECTION); ??????? cell.setCellStyle(cellstyle); ??? } } |
我們看一下點(diǎn)擊了,生成excel文件的效果圖
看看我們生成的excel文件
數(shù)據(jù)庫表結(jié)構(gòu)如下
| create database poi_mvc use poi_mvc create table detial( sjhm varchar(20),? --手機(jī)號(hào)碼 hjlx varchar(20),?? --呼叫類型 dfhm varchar(20),? --對(duì)方號(hào)碼 qssj varchar(20),?? --起始時(shí)間 thsc varchar(20),?? --通話時(shí)長 thdd varchar(20),?? --通話地點(diǎn) ctlx varchar(20),??? --長途類型 jbhf varchar(20),?? --基本話費(fèi) chf varchar(20),??? --長話費(fèi) zhf varchar(20),??? --總話費(fèi) ) |
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
Apache :POI-HSSF
·???????? 字體大小:
·???????? 小
·???????? 中
·???????? 大
?
Jakarta_POI 使用Java讀寫Excel(97-2002)文件,可以滿足大部分的需要。
因?yàn)閯偤糜幸粋€(gè)項(xiàng)目使用到了這個(gè)工具,花了點(diǎn)時(shí)間順便翻譯了一下POI本身
帶的一個(gè)Guide.有一些節(jié)減和修改,希望給使用這個(gè)項(xiàng)目的人一些入門幫助。
POI 下面有幾個(gè)自項(xiàng)目:HSSF用來實(shí)現(xiàn)Excel 的讀寫.以下是HSSF的主頁
http://jakarta.apache.org/poi/hssf/index.html
下面的介紹是基于以下地址的翻譯:
http://jakarta.apache.org/poi/hssf/quick-guide.html
目前的版本為1.51應(yīng)該是很長時(shí)間之內(nèi)的一個(gè)穩(wěn)定版,但HSSF提供的Sample不是基于
1.51所寫,所以使用的時(shí)候需要適當(dāng)?shù)淖⒁?/span>.
其實(shí)POI下面的幾個(gè)子項(xiàng)目側(cè)重不同讀寫 Word 的HDF正在開發(fā)當(dāng)中.
XML下的FOP(http://xml.apache.org/fop/index.html)
可以輸出pdf文件,也是比較好的一個(gè)工具
目錄:
創(chuàng)建一個(gè)workbook
創(chuàng)建一個(gè)sheet
創(chuàng)建cells
創(chuàng)建日期cells
設(shè)定單元格格式
說明:
以下可能需要使用到如下的類
import org.apache.poi.hssf.usermodel.HSSFCell;
import org.apache.poi.hssf.usermodel.HSSFCellStyle;
import org.apache.poi.hssf.usermodel.HSSFDataFormat;
import org.apache.poi.hssf.usermodel.HSSFFont;
import org.apache.poi.hssf.usermodel.HSSFRow;
import org.apache.poi.hssf.usermodel.HSSFSheet;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.hssf.util.HSSFColor;
創(chuàng)建workbook
HSSFWorkbook wb = new HSSFWorkbook();
//使用默認(rèn)的構(gòu)造方法創(chuàng)建workbook
FileOutputStream fileOut = new FileOutputStream("workbook.xls");
//指定文件名
wb.write(fileOut);
//輸出到文件
fileOut.close();
創(chuàng)建一個(gè)sheet
HSSFWorkbook wb = new HSSFWorkbook();
HSSFSheet sheet1 = wb.createSheet("new sheet");
//workbook創(chuàng)建sheet
HSSFSheet sheet2 = wb.createSheet("second sheet");
//workbook創(chuàng)建另外的sheet
FileOutputStream fileOut = new FileOutputStream("workbook.xls");
wb.write(fileOut);
fileOut.close();
創(chuàng)建cells
HSSFWorkbook wb = new HSSFWorkbook();
HSSFSheet sheet = wb.createSheet("new sheet");
//注意以下的代碼很多方法的參數(shù)是short 而不是int 所以需要做一次類型轉(zhuǎn)換
HSSFRow row = sheet.createRow((short)0);
//sheet 創(chuàng)建一行
HSSFCell cell = row.createCell((short)0);
//行創(chuàng)建一個(gè)單元格
cell.setCellValue(1);
//設(shè)定單元格的值
//值的類型參數(shù)有多中double ,String ,boolean,
row.createCell((short)1).setCellValue(1.2);
row.createCell((short)2).setCellValue("This is a string");
row.createCell((short)3).setCellValue(true);
// Write the output to a file
FileOutputStream fileOut = new FileOutputStream("workbook.xls");
wb.write(fileOut);
fileOut.close();
創(chuàng)建日期cells
HSSFWorkbook wb = new HSSFWorkbook();
HSSFSheet sheet = wb.createSheet("new sheet");
HSSFRow row = sheet.createRow((short)0);
HSSFCell cell = row.createCell((short)0);
//設(shè)定值為日期
cell.setCellValue(new Date());
HSSFCellStyle cellStyle = wb.createCellStyle();
//指定日期顯示格式
cellStyle.setDataFormat(HSSFDataFormat.getFormat("m/d/yy h:mm"));
cell = row.createCell((short)1);
cell.setCellValue(new Date());
//設(shè)定單元格日期顯示格式
cell.setCellStyle(cellStyle);
FileOutputStream fileOut = new FileOutputStream("workbook.xls");
wb.write(fileOut);
fileOut.close();
設(shè)定單元格格式
單元格格式的設(shè)定有很多形式包括單元格的對(duì)齊方式,內(nèi)容的字體設(shè)置,
單元格的背景色等,因?yàn)樾问奖容^多,只舉一些例子.以下的例子在
POI1.5中可能會(huì)有所改變具體查看API.
..........
// Aqua background
HSSFCellStyle style = wb.createCellStyle();
//創(chuàng)建一個(gè)樣式
style.setFillBackgroundColor(HSSFCellStyle.AQUA);
//設(shè)定此樣式的的背景顏色填充
style.setFillPattern(HSSFCellStyle.BIG_SPOTS);
//樣式的填充類型。
//有多種式樣如:
//HSSFCellStyle.BIG_SPOTS
//HSSFCellStyle.FINE_DOTS
//HSSFCellStyle.SPARSE_DOTS等
style.setAlignment(HSSFCellStyle.ALIGN_CENTER );
//居中對(duì)齊
style.setFillBackgroundColor(HSSFColor.GREEN.index);
//設(shè)定單元個(gè)背景顏色
style.setFillForegroundColor(HSSFColor.RED.index);
//設(shè)置單元格顯示顏色
HSSFCell cell = row.createCell((short) 1);
cell.setCellValue("X");
cell.setCellStyle(style);?
?
?
使用poi的hssf生成一個(gè)excel文件以后
有一個(gè)主類Workbook(相當(dāng)于一個(gè)excel文件)的方法
Workbook.write(OutputStream)可以寫到response.getOutputStream()里面
如果事先設(shè)置response的contentType為excel和下載的附件名稱就可下載excel
其中expertExcel無非是從數(shù)據(jù)庫或者其他地方獲取數(shù)據(jù)創(chuàng)建excel即可.
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
POI讀取EXCEL教程
一、Excel基礎(chǔ)?
二、HSSF概況?
三、通過usermodel讀取文件?
四、通過usermodel寫入文件?
五、通過eventusermodel讀取文件?
六、HSSF電子表格結(jié)構(gòu)?
七、通過HPSF讀取文檔屬性?
八、文檔摘要信息?
九、附錄?
━━━━━━?
正文:?
━━━━━━?
在上一篇文章中,我們介紹了POI項(xiàng)目的基本概念,了解了如何用POI來讀寫OLE 2復(fù)合文檔結(jié)構(gòu),并給出了兩個(gè)簡(jiǎn)單的例子:用POI來讀寫Excel文件的Workbook流。本文繼續(xù)前文的話題,闡述如何用POI來讀取/寫入完整的Excel文件。?
約定:POI項(xiàng)目2.0版現(xiàn)在已經(jīng)接近正式發(fā)行階段,開發(fā)進(jìn)度迅速,不斷有新的功能集成到原有的系統(tǒng),同時(shí)也有對(duì)原有系統(tǒng)的修改。為了保證本文的及時(shí)性,本文將按照最近的1.9開發(fā)版說明。雖然編譯最近的發(fā)行版源代碼也能正常運(yùn)行,但現(xiàn)在的代碼和2.0的發(fā)行版會(huì)有一些出入。?
一、Excel基礎(chǔ)?
Microsoft Excel 97文件格式也被稱為BIFF8,最近版本的Excel只對(duì)該格式作了少量的改動(dòng)。增加對(duì)新格式的支持除了增加項(xiàng)目的復(fù)雜性之外,唯一的效果也許只是不得不使每個(gè)用戶升級(jí)代碼,沒有什么實(shí)際的好處。因此,在下文說明中,凡是提到Excel 97格式的地方其實(shí)都是指Excel從97到XP的格式。?
二、HSSF概況?
POI項(xiàng)目實(shí)現(xiàn)的Excel 97文件格式稱為HSSF——也許你已經(jīng)猜到,HSSF是Horrible SpreadSheet Format的縮寫,也即“討厭的電子表格格式”(微軟使某些原本簡(jiǎn)單的事情過分復(fù)雜,同時(shí)又過分簡(jiǎn)單地處理了某些原本需要靈活性的事情,讓人不勝佩服!)也許HSSF的名字有點(diǎn)滑稽,就本質(zhì)而言它是一個(gè)非常嚴(yán)肅、正規(guī)的API。通過HSSF,你可以用純Java代碼來讀取、寫入、修改Excel文件。?
前面一篇文章提到了POIFS,那么HSSF和POIFS又有什么關(guān)系呢?就象其他POI的API一樣,HSSF建立在POIFS的基礎(chǔ)上,因此在HSSF內(nèi)的有些代碼和前文的某些代碼很相似。不過,當(dāng)我們編寫基于HSSF API的代碼時(shí),一般不需要了解POIFS API的細(xì)節(jié)。?
HSSF為讀取操作提供了兩類API:usermodel和eventusermodel,即“用戶模型”和“事件-用戶模型”。前者很好理解,后者比較抽象,但操作效率要高得多。usermodel主要有org.apache.poi.hssf.usermodel和org.apache.poi.hssf.eventusermodel包實(shí)現(xiàn)(在HSSF的早期版本中,org.apache.poi.hssf.eventusermodel屬于eventmodel包)。?
usermodel包把Excel文件映射成我們熟悉的結(jié)構(gòu),諸如Workbook、Sheet、Row、Cell等,它把整個(gè)結(jié)構(gòu)以一組對(duì)象的形式保存在內(nèi)存之中。eventusermodel要求用戶熟悉文件格式的底層結(jié)構(gòu),它的操作風(fēng)格類似于XML的SAX API和AWT的事件模型(這就是eventusermodel名稱的起源),要掌握竅門才能用好。另外,eventusermodel的API只提供讀取文件的功能,也就是說不能用這個(gè)API來修改文件。?
三、通過usermodel讀取文件?
用HSSF的usermodel讀取文件很簡(jiǎn)單。首先創(chuàng)建一個(gè)InputStream,然后創(chuàng)建一個(gè)HSSFWorkbook:?
InputStream myxls = new FileInputStream("workbook.xls"));
HSSFWorkbook wb???? = new HSSFWorkbook(myxls);
有了HSSFWorkbook實(shí)例,接下來就可以提取工作表、工作表的行和列,例如:?
HSSFSheet sheet = wb.getSheetAt(0);?????? // 第一個(gè)工作表
HSSFRow row???? = sheet.getRow(2);??????? // 第三行
HSSFCell cell?? = row.getCell((short)3);? // 第四個(gè)單元格
上面這段代碼提取出第一個(gè)工作表第三行第四單元格。利用單元格對(duì)象可以獲得它的值,提取單元格的值時(shí)請(qǐng)注意它的類型:?
if (cell.getCellType() == HSSFCell.CELL_TYPE_STRING) {
??? ("單元格是字符串,值是: " + cell.getStringCellValue());
} else if (cell.getCellType() == HSSFCell.CELL_TYPE_NUMERIC) {
??? ("單元格是數(shù)字,值是: " + cell.getCellValue());
} else () {
??? ("單元格的值不是字符串或數(shù)值。");
}
如果搞錯(cuò)了數(shù)據(jù)類型,程序?qū)⒂龅疆惓!L貏e地,用HSSF處理日期數(shù)據(jù)要小心。Excel內(nèi)部以數(shù)值的形式保存日期數(shù)據(jù),區(qū)別日期數(shù)據(jù)的唯一辦法是通過單元格的格式(如果你曾經(jīng)在Excel中設(shè)置過日期格式,應(yīng)該明白這是什么意思)。?
因此,對(duì)于包含日期數(shù)據(jù)的單元格,cell.getCellType()將返回HSSFCell.CELL_TYPE_NUMERIC,不過利用工具函數(shù)HSSFDateUtil.isCellDateFormatted(cell)可以判斷出單元格的值是否為日期。isCellDateFormatted函數(shù)通過比較單元格的日期和Excel的內(nèi)置日期格式得出結(jié)論——可以想象,按照這種判斷方法,很多時(shí)候isCellDateFormatted函數(shù)會(huì)返回否定的結(jié)論,存在一定的誤判可能。?
本文附錄包含了一個(gè)在Servlet環(huán)境中利用HSSF創(chuàng)建和返回Excel工作簿的實(shí)例。?
四、通過usermodel寫入文件?
寫入XLS文件比讀取XLS文件還要簡(jiǎn)單。創(chuàng)建一個(gè)HSSFWorkbook實(shí)例,然后在適當(dāng)?shù)臅r(shí)候創(chuàng)建一個(gè)把文件寫入磁盤的OutputStream,但延遲到處理結(jié)束時(shí)創(chuàng)建OutputStream也可以:?
HSSFWorkbook wb = new HSSFWorkbook();
FileOutputStream fileOut
= new FileOutputStream("workbook.xls");
wb.write(fileOut);
fileOut.close();
創(chuàng)建工作表及其內(nèi)容必須從相應(yīng)的父對(duì)象出發(fā),例如:?
HSSFSheet sheet = wb.createSheet();
HSSFRow row???? = sheet.createRow((short)0);?
HSSFCell cell?? = row.createCell((short)0);?
cell.setCellValue(1);?
row.createCell((short)1).setCellValue(1.2);?
row.createCell((short)2).setCellValue("一個(gè)字符串");
row.createCell((short)3).setCellValue(true);
如果要設(shè)置單元格的樣式,首先要?jiǎng)?chuàng)建一個(gè)樣式對(duì)象,然后把它指定給一個(gè)單元格——或者把它指定給多個(gè)具有相同樣式的單元格,例如,如果Excel表格中有一個(gè)摘要行,摘要行的數(shù)據(jù)必須是粗體、斜體,你可以創(chuàng)建一個(gè)summaryRowStyle樣式對(duì)象,然后把這個(gè)樣式指定給所有摘要行上的單元格。?
注意,CellFormat和CellStyle對(duì)象是工作簿對(duì)象的成員,單元格對(duì)象只是引用它們。?
...
HSSFCellStyle style = workbook.createCellStyle();
style.setDataFormat
(HSSFDataFormat.getBuiltinFormat("($#,##0_);[Red]($#,##0)"));
style.setFillBackgroundColor(HSSFColor.AQUA.index);
style.setFillPattern(HSSFCellStyle.BIG_SPOTS);
...
someCell.setCellStyle(style);
someOtherCell.setCellStyle(style);
版本較新的HSSF允許使用數(shù)量有限的Excel公式。這一功能目前還是“Beta級(jí)質(zhì)量”,正式使用之前務(wù)必仔細(xì)測(cè)試。指定公式的方式類如:someCell.setCellFormula(SUM(A1:A2:);。?
當(dāng)前,公式中已經(jīng)可以調(diào)用所有內(nèi)建的函數(shù)或操作符,但邏輯操作符和函數(shù)(例如IF函數(shù))除外,這部分功能目前還在開發(fā)之中。?
五、通過eventusermodel讀取文件?
通過eventusermodel讀取文件要比使用usermodel復(fù)雜得多,但效率也要高不少,因?yàn)樗髴?yīng)用程序一邊讀取數(shù)據(jù),一邊處理數(shù)據(jù)。eventusermodel實(shí)際上模擬了DOM環(huán)境下SAX處理XML文檔的辦法,應(yīng)用程序首先要注冊(cè)期望處理的數(shù)據(jù),eventusermodel將在遇到匹配的數(shù)據(jù)結(jié)構(gòu)時(shí)回調(diào)應(yīng)用程序注冊(cè)的方法。使用eventusermodel最大的困難在于你必須熟悉Excel工作簿的內(nèi)部結(jié)構(gòu)。?
在HSSF中,低層次的二進(jìn)制結(jié)構(gòu)稱為記錄(Record)。記錄有不同的類型,每一種類型由org.apache.poi.hssf.record包中的一個(gè)Java類描述。例如,BOFRecord記錄表示Workbook或Sheet區(qū)域的開始,RowRecord表示有一個(gè)行存在并保存其樣式信息。所有具有CellValueRecordInterface接口的記錄表示Excel的單元格,包括NumericRecord、LabelSSTRecord和FormulaRecord(還有其他一些,其中部分已被棄置不用,部分用于優(yōu)化處理,但一般而言,HSSF可以轉(zhuǎn)換它們)。?
下面是一個(gè)注冊(cè)事件處理句柄的例子:?
private EventRecordFactory factory = new EventRecordFactory();
factory.registerListener(new ERFListener() {
??? public boolean processRecord(Record rec) {
??????? (got BOF Record);??????????????
??????? return true;??????????????
??? }
}, new short[] {BOFRecord.sid});
factory.processRecords(someInputStream);
六、HSSF電子表格結(jié)構(gòu)?
如前所述,HSSF建立在POIFS的基礎(chǔ)上。具體地說,Excel 97+文件是OLE 2復(fù)合文檔( OLE 2 Compound Document),底層的OLE 2復(fù)合文檔保存了一個(gè)總是命名為Workbook(Excel 95除外,HSSF不支持Excel 95)的流。然而,宏和圖片并不保存在Workbook流,它們有自己獨(dú)立的流,有時(shí)甚至?xí)诺?/span>OLE 2 CDF文件之內(nèi)的另一個(gè)目錄。理想情況下,宏也應(yīng)該被保留,不過目前POI項(xiàng)目中還沒有合適的API來處理宏。?
每一個(gè)流之內(nèi)是一組記錄,一個(gè)記錄其實(shí)就是一個(gè)字節(jié)數(shù)組,可分為記錄頭、記錄體兩部分。記錄頭指明了記錄的類型(也即ID)以及后繼數(shù)據(jù)的長度,記錄體被分割成多個(gè)字段(Field),字段包含數(shù)值數(shù)據(jù)(包括對(duì)其他記錄的引用)、字符數(shù)據(jù)或標(biāo)記。?
下圖概要說明了Excel工作簿的頂級(jí)結(jié)構(gòu):?
Bla.xls {
??? OLE2CDF headers
??? "Workbook" stream {
??????? Workbook {
??????????? Static String Table Record..
??????????? Sheet names… and?pointers
??????? }?
??????? Sheet {
??????????? ROW
??????????? ROW
??????????? …
??????????? NUMBER RECORD (cell)
??????????? LABELSST Record (cell)
??????????? …
??????? }
??????? Sheet
??? }
}
… images, macros, etc.
Document Summary
Summary
七、通過HPSF讀取文檔屬性?
在Microsoft Word、Excel、PowerPoint等軟件中,用戶可以通過“文件”→“屬性”菜單給文檔添加附加信息,包括文檔的標(biāo)題、主題、摘要、類別、關(guān)鍵詞等,同時(shí)應(yīng)用軟件本身還會(huì)加入最后訪問的用戶、最后訪問和修改/打印的日期時(shí)間等信息。?
文檔的屬性和正文是分開保存的。如前所述,OLE 2 CDF文件內(nèi)部就象是一個(gè)容器,里面包含許多類似目錄和文件的結(jié)構(gòu),而POIFS就是用來訪問其中的文件的工具。這些文件也稱為流,文檔的屬性就保存在POIFS文件系統(tǒng)中專用的流里面。以一個(gè)Word文檔為例:雖然在資源管理器中你只看到一個(gè)叫做MyFile.doc的文檔,其實(shí)在這個(gè)文檔的內(nèi)部,又包含了一個(gè)WordDocument、一個(gè)SummaryInformation和一個(gè)DocumentSummaryInformation文檔;通常還會(huì)有其他的文檔,這里暫且不管。?
你能夠猜出這些文檔(流)分別包含什么內(nèi)容嗎?不錯(cuò),WordDocument包含了你在Word里面編輯的文本,文檔的屬性保存在SummaryInformation和DocumentSummaryInformation流里面。也許將所有屬性保存在單個(gè)文檔里面看起來太簡(jiǎn)單了,所以Microsoft決心要使用兩個(gè)流,為了使事情更復(fù)雜一點(diǎn),這兩個(gè)流的名字前面還加上了八進(jìn)制的\005字符——這是一個(gè)不可打印的字符,因此前面就把它省略了。?
Microsoft定義的標(biāo)準(zhǔn)屬性有一個(gè)好處,它們并不在乎主文檔到底是什么類型——不管是Word文檔、Excel工作簿還是PowerPoint幻燈。只要你知道如何讀取Excel文檔的屬性,就知道了如何讀取其他文檔的屬性。?
讀取文檔屬性其實(shí)并不復(fù)雜,因?yàn)?/span>Java程序可以利用POI項(xiàng)目的HPSF包。HPSF是 Horrible Property Set Format的縮寫,譯成中文就是“討厭的屬性集格式”。HPSF包是POI項(xiàng)目實(shí)現(xiàn)的讀取屬性工具,目前還不支持屬性寫入。?
對(duì)于讀取Microsoft定義的標(biāo)準(zhǔn)屬性,通過HPSF提供的API可以很方便地辦到;但如果要讀取任意屬性集就要用到更一般化的API,可以想象它要比讀取標(biāo)準(zhǔn)屬性的API復(fù)雜不少。本文只介紹讀取標(biāo)準(zhǔn)屬性的簡(jiǎn)單API,因?yàn)閷?duì)大多數(shù)應(yīng)用程序來說這已經(jīng)完全足夠了。?
下面就是一個(gè)讀取OLE 2 CDF文檔的標(biāo)題(title)屬性的Java程序:?
import java.io.*;
import org.apache.poi.hpsf.*;
import org.apache.poi.poifs.eventfilesystem.*;
/**
* 讀取OLE 2文檔標(biāo)題的示例程序,
* 在命令行參數(shù)中指定文檔的文件名字。
*/
public class ReadTitle
{
??? public static void main(String[] args) throws IOException
??? {
??????? final String filename = args[0];
????????POIFSReader r???????? = new?POIFSReader();
??????? r.registerListener(new MyPOIFSReaderListener(),
??????????? "\005SummaryInformation");
??????? r.read(new FileInputStream(filename));
??? }
??? static class MyPOIFSReaderListener
implements?POIFSReaderListener
??? {
??????? public void processPOIFSReaderEvent(POIFSReaderEvent event)
??????? {
??????????? SummaryInformation si = null;
??????????? try
??????????? {
??????????????? si = (SummaryInformation)
??????????????? PropertySetFactory.create(event.getStream());
??????????? }
??????????? catch (Exception ex)
??????????? {
??????????????? throw new RuntimeException
??????????????????? ("屬性集流\"" + event.getPath() +
??????????????????????? event.getName() + "\": " + ex);
??????????? }
??????????? final String title = si.getTitle();
??????????? if (title != null)
??????????????? System.out.println("標(biāo)題: \"" + title + "\"");
??????????? else
??????????????? System.out.println("該文檔沒有標(biāo)題.");
??????????? }
??????? }
}
main()方法利用POIFS的事件系統(tǒng)從命令行指定的OLE 2文檔讀取名為\005SummaryInformation的流,當(dāng)POIFSReader 遇到這個(gè)流時(shí),它把控制傳遞給MyPOIFSReaderListener的processPOIFSReaderEvent()方法。?
processPOIFSReaderEvent() 到底有什么用呢?它通過參數(shù)獲得一個(gè)輸入流,該輸入流包含了文檔標(biāo)題等屬性。為了訪問文檔的屬性,我們從輸入流創(chuàng)建一個(gè)PropertySet實(shí)例,如下所示:?
si = (SummaryInformation) PropertySetFactory.create(event.getStream());
這個(gè)語句其實(shí)包含三個(gè)步驟的操作:?
◆ event.getStream()從POIFSReader傳入的POIFSReaderEvent獲得輸入流。?
◆以剛才獲得的輸入流為參數(shù),調(diào)用PropertySetFactory的靜態(tài)方法create()。正如其名字所暗示的,PropertySetFactory是一個(gè)工廠類,它有一臺(tái)“機(jī)器”能夠把一個(gè)輸入流轉(zhuǎn)換成一個(gè)PropertySet實(shí)例,這臺(tái)機(jī)器就是create()方法。?
◆把create()方法返回的PropertySet定型(cast)成為SummaryInformation。PropertySet提供了按照一般辦法讀取屬性集的各種機(jī)制,SummaryInformation是PropertySet的子類,即SummaryInformation類在PropertySet類的基礎(chǔ)上增加了操作Microsoft標(biāo)準(zhǔn)屬性的便捷方法。?
在這個(gè)處理過程中,可能引起錯(cuò)誤的因素很多,因此我們把這部分內(nèi)容放入了一個(gè)try塊——不過這個(gè)示例程序只按照最簡(jiǎn)單的方式處理了異常,在實(shí)際應(yīng)用中,最好能夠?qū)赡艹霈F(xiàn)的不同異常類型分別處理。除了一般的I/O異常之外,還有可能遇到HPSF特有的異常,例如,如果輸入流不包含屬性集或?qū)傩约欠?#xff0c;就會(huì)拋出NoPropertySetStreamException異常。?
有一種錯(cuò)誤不太常見,但也不是絕無可能——\005SummaryInformation包含一個(gè)合法的屬性集,但不是摘要信息屬性集。如果出現(xiàn)這種情況,則定型成SummaryInformation操作會(huì)失敗,引發(fā)ClassCastException異常。?
獲得SummaryInformation實(shí)例之后,剩下的事情就很簡(jiǎn)單了,只要調(diào)用getTitle()方法,然后輸出結(jié)果。?
除了getTitle()之外,SummaryInformation還包含其他一些便捷方法,例如getApplicationName()、getAuthor()、getCharCount()、和getCreateDateTime()等。HPSF的JavaDoc文檔詳細(xì)說明了所有這些方法。?
八、文檔摘要信息?
遺憾的是,并非所有的屬性都保存在摘要信息屬性集之中。許多(但不是全部)OLE 2文件還有另一個(gè)屬性集,稱為“文檔摘要信息”,對(duì)應(yīng)的流是\005DocumentSummaryInformation。這個(gè)屬性集保存的屬性包括文檔的類別、PowerPoint幻燈的多媒體剪輯數(shù)量,等等。?
要訪問文檔摘要信息屬性集,程序的處理過程也和上例相似,只是注冊(cè)的目標(biāo)應(yīng)該改成\005DocumentSummaryInformation流——有時(shí),你可能想要同時(shí)注冊(cè)到摘要信息和文檔摘要信息這兩個(gè)流。其余的處理方式和前面的例子差不多,你應(yīng)該把包含文檔摘要信息的流傳遞給PropertySetFactory.create(),但這次工廠方法將返回一個(gè)DocumentSummaryInformation對(duì)象(而不是前面例子中的SummaryInformation對(duì)象)。如果同時(shí)注冊(cè)到了兩個(gè)流,注意檢查返回值的具體類型,或者使用Java的instanceof操作符,或者使用專用的isSummaryInformation()和isDocumentSummaryInformation()方法。記住,create()方法返回的總是一個(gè)PropertySet對(duì)象,因此你總是可以對(duì)create()返回對(duì)象調(diào)用isSummaryInformation()和isDocumentSummaryInformation()方法,PropertySet類之所以要提供這兩個(gè)方法,是因?yàn)閷傩约赡苁亲远x的。?
如果你想要處理自定義的屬性集,或者要從標(biāo)準(zhǔn)的屬性集讀取用戶定義的屬性,必須使用一個(gè)更一般化的API,前面已經(jīng)提到,這個(gè)API要復(fù)雜得多,本文不再討論,請(qǐng)參見HPSF的HOW-TO文檔和POI的文檔。?
結(jié)束語:本文探討了HSSF的應(yīng)用以及如何輸出到Excel文件,另外還涉及了HPSF以及如何讀取屬性集文檔摘要信息。POI是一個(gè)功能非常強(qiáng)大的項(xiàng)目,許多主題本文尚未涉及,例如如何用HSSF Serializer將XML文檔轉(zhuǎn)換成Excel格式等,這一切就有待你自己去研究了。?
參考:?
Jakarta?POI項(xiàng)目主頁?
Jakarta?POI?源代碼?
九、附錄?
實(shí)例:利用Servlet創(chuàng)建和返回一個(gè)工作簿。?
package org.apache.poi.hssf.usermodel.examples;
import java.io.*;
import java.net.*;
import javax.servlet.*;
import javax.servlet.http.*;
import org.apache.poi.hssf.usermodel.*;
public class HSSFCreate extends HttpServlet {
??? public void init(ServletConfig config)
throws ServletException {
??????? super.init(config);??
??? }
??? public void destroy() {
??? }
??? /** 處理HTTP GET 和POST請(qǐng)求
???? * @param request:請(qǐng)求
???? * @param response:應(yīng)答
???? */
??? protected void processRequest(HttpServletRequest request,
??????? HttpServletResponse response)
throws ServletException, IOException {
??????? response.setContentType("application/vnd.ms-excel");
??????? HSSFWorkbook wb = new HSSFWorkbook();
??????? HSSFSheet sheet = wb.createSheet("new sheet");
??????? // 創(chuàng)建一個(gè)新的行,添加幾個(gè)單元格。
??????? // 行號(hào)從0開始計(jì)算
??????? HSSFRow row???? = sheet.createRow((short)0);
??????? // 創(chuàng)建一個(gè)單元格,設(shè)置單元格的值
??????? HSSFCell cell?? = row.createCell((short)0);
??????? cell.setCellValue(1);
??????? row.createCell((short)1).setCellValue(1.2);
??????? row.createCell((short)2).setCellValue("一個(gè)字符串值");
??????? row.createCell((short)3).setCellValue(true);
??????? // 寫入輸出結(jié)果
??????? OutputStream out = response.getOutputStream();
??????? wb.write(out);
??????? out.close();
??? }
??? /** 處理HTTP GET請(qǐng)求
???? * @param request:請(qǐng)求
???? * @param response:應(yīng)答
???? */
??? protected void doGet(HttpServletRequest request,
??????? HttpServletResponse response)
throws ServletException, IOException {
??????? processRequest(request, response);
??? }
??? /** 處理HTTP POST請(qǐng)求
???? * @param request:請(qǐng)求
???? * @param response:應(yīng)答
???? */
??? protected void doPost(HttpServletRequest request,
??????? HttpServletResponse response)
throws ServletException, IOException {
??????? processRequest(request, response);
??? }
??? /** 返回關(guān)于Servlet的簡(jiǎn)單說明
???? */
??? public String getServletInfo() {
?????? return "示例:在Servlet中用HSSF創(chuàng)建Excel工作簿";
??? }
}
?
?
?
?
?
POI?HSSF 操作MS Excel簡(jiǎn)述 (OMIS二期設(shè)計(jì)階段尋找Excel導(dǎo)入導(dǎo)出實(shí)現(xiàn)方法)
POI?HSSF 操作MS Excel簡(jiǎn)述
POI?HSSF是一個(gè)專門操作EXCEL的java包,可通過純java操作xls文件。
POI?HSSF的類文件都放在在org.apache.poi.hssf包下,通過此包中的類就可實(shí)現(xiàn)用java操作Excel文件了。
下面是用POI?HSSF操作Excel文件的方法簡(jiǎn)述:
一,建立Excel工作薄
HSSFWorkbook wb = new HSSFWorkbook();
二,建立Excel工作表,每個(gè)工作表對(duì)應(yīng)的是Excel界面左下角的一個(gè)標(biāo)簽sheet1,sheet2 …
HSSFSheet sheet1 = wb.createSheet("new sheet");
三,在工作表中建立單元格
//首先,建立行對(duì)像,行號(hào)作為參數(shù)傳給createRow方法,第一行由0開始計(jì)算。
HSSFRow row = sheet.createRow((short)0);
//建單元格
HSSFCell cell = row.createCell((short)0);
//給單元格賦值
cell.setCellValue(1);
//也可同一行內(nèi)完成建立單元格和賦值
row.createCell((short)1).setCellValue(1.2);
row.createCell((short)2).setCellValue("This is a string");
row.createCell((short)3).setCellValue(true);
//數(shù)據(jù)格式可通過創(chuàng)建單元格值時(shí)默認(rèn)如上面所視
//也可以創(chuàng)建單元格后調(diào)用setCellType指定
cell.setCellType(CELL_TYPE_NUMERIC);
四,向單元格插入日期值
HSSFWorkbook wb = new HSSFWorkbook();
HSSFSheet sheet = wb.createSheet("new sheet");
// 可通過Sheet.setSheetName(sheetindex,"SheetName",encoding)設(shè)定工作表名
// 創(chuàng)建新行并向其加入單元格,行號(hào)由0開始。
HSSFRow row = sheet.createRow((short)0);
// 創(chuàng)建一個(gè)單元格并向其輸入一日期值,但這第一個(gè)單元格并非是日期格式。
HSSFCell cell = row.createCell((short)0);
cell.setCellValue(new Date());
// 我們將這第二個(gè)單元格改成日期格式,這需要從工作薄創(chuàng)建一個(gè)新的單元格格式,這可// 以只影響當(dāng)前建立的一個(gè)單元格。
HSSFCellStyle cellStyle = wb.createCellStyle();
cellStyle.setDataFormat(HSSFDataFormat.getBuiltinFormat("m/d/yy h:mm"));
cell = row.createCell((short)1);
cell.setCellValue(new Date());
cell.setCellStyle(cellStyle);
五,各種單元格樣式
HSSFCellStyle cellStyle = wb.createCellStyle();
//對(duì)齊
cellStyle.setAlignment(HSSFCellStyle.ALIGN_CENTER);
//帶邊框
cellStyle.setBorderBottom(HSSFCellStyle.BORDER_THIN);
//顏色與填充樣式
cellStyle.setFillBackgroundColor(HSSFColor.AQUA.index);
cellStyle.setFillPattern(HSSFCellStyle.BIG_SPOTS);
cellStyle.setFillForegroundColor(HSSFColor.ORANGE.index);
cellStyle.setFillPattern(HSSFCellStyle.SOLID_FOREGROUND);
六,行高,列寬。
HSSFWorkbook wb = new HSSFWorkbook();
HSSFSheet sheet = wb.createSheet("new sheet");
HSSFRow row = sheet.createRow((short)0);
//2是行高值
row.setRowHeight(2);
//3是列號(hào),4是列寬值
sheet.setColumnWidth(3, 4);
七,例程
首先調(diào)用一個(gè)方法將Oracle數(shù)據(jù)庫中的數(shù)據(jù)取出,放到List實(shí)例中,這里我調(diào)用了srrd項(xiàng)目中ProductData類的listProductQuery()取得一個(gè)List實(shí)例。List中的對(duì)象是一系列名為ProductQuery實(shí)體類的實(shí)例。然后讀List,將ProductQuery實(shí)例中的數(shù)據(jù)取出放到HSSFCell單元格中。最后將HSSFWorkbook中的數(shù)據(jù)輸出到輸出流,完成數(shù)據(jù)導(dǎo)出。
//建工作薄
HSSFWorkbook wb = new HSSFWorkbook();
//建名為example的工作表
HSSFSheet sheet = wb.createSheet("example");
//給工作表前8列定義列寬
sheet.setColumnWidth((short)0,(short)2500);
sheet.setColumnWidth((short)1,(short)6000);
sheet.setColumnWidth((short)2,(short)3500);
sheet.setColumnWidth((short)3,(short)9000);
sheet.setColumnWidth((short)4,(short)8000);
sheet.setColumnWidth((short)5,(short)8000);
sheet.setColumnWidth((short)6,(short)20000);
sheet.setColumnWidth((short)7,(short)8000);
//在表中建行
HSSFRow row = sheet.createRow(0);
//建立單元格?
HSSFCell cell[] = new HSSFCell[8];
for (short i = 0; i < 8; i++) {
cell?= row.createCell(i);
//將單元格定義成UTF_16編碼,這樣才能使輸出數(shù)據(jù)不會(huì)亂碼
cell.setEncoding(HSSFCell.ENCODING_UTF_16);
}
//寫單元格標(biāo)題
cell[0].setCellValue("登記ID");
cell[1].setCellValue("登記號(hào)");
cell[2].setCellValue("所在地市ID");
cell[3].setCellValue("產(chǎn)品中文名");
cell[4].setCellValue("產(chǎn)品英文名");
cell[5].setCellValue("產(chǎn)品服務(wù)對(duì)象");
cell[6].setCellValue("產(chǎn)品功能描述");
cell[7].setCellValue("產(chǎn)品類別");
//查詢數(shù)據(jù)庫,取得數(shù)據(jù)列表的List實(shí)例
List list = new ArrayList();
ProductDataManager mgr = new ProductDataManager();
try {
list = mgr.listProductQuery("","", "", "", "", "1999-2-1", "2004-2-1");
} catch (SrrdException e) {
e.printStackTrace();?
}
//從List中取出數(shù)據(jù)放入工作表中
if (list != null && list.size() > 0) {
for (int i = 0; i < list.size() - 1; i++) {
ProductQuery query = (ProductQuery) list.get(i);
HSSFRow datarow = sheet.createRow(i + 1);
HSSFCell data[] = new HSSFCell[8];
for (short j = 0; j < 8; j++) {
data[j] = datarow.createCell(j);
//將單元格定義成UTF_16編碼,這樣才能使輸出數(shù)據(jù)不會(huì)亂碼
data[j].setEncoding(HSSFCell.ENCODING_UTF_16);
}
data[0].setCellValue(query.getCertId());
data[1].setCellValue(query.getCertNum());
data[2].setCellValue(query.getCityCode());
data[3].setCellValue(query.getSoftWareCname());
data[4].setCellValue(query.getSoftWareEname());
data[5].setCellValue(query.getSoftwareFor());
data[6].setCellValue(query.getSoftwareFuncDesc());
data[7].setCellValue(query.getSoftwareType());
}
}
//將工作薄輸出到輸出流
ServletOutputStream sos=response.getOutputStream();
wb.write(sos);
sos.close();
//也可輸出成xls文件
File file = new File("workbook.xls");
try {
FileOutputStream fileOut = new FileOutputStream(file);
wb.write(fileOut);
fileOut.close();
} catch (IOException e) {
e.printStackTrace();?
}
?
?
?
?
?
?
?
?
?
?
總結(jié)
- 上一篇: 在Linux下用netstat查看网络状
- 下一篇: HSSFDataFormat大全