poi
-----
這是導(dǎo)出時,容易出現(xiàn)的問題,后面說到
設(shè)置字符編碼:
Font font =wb.createFont();
font.setCharSet(font.ANSI_CHARSET);//注意這地方不能手寫數(shù)字,這是常量
開始正文
一、黑歷史
為什么要做excel打印:就是為了客戶需求,有的客戶習(xí)慣excel。
java操作excel主要分兩類:
1、(全方位操作用)poi大概就是:屬于apache的產(chǎn)品,操作microsoft excel word,ppt,visio等微軟旗下所有的工具,支持office所有版本。
但是在poi早期,當(dāng)時微軟產(chǎn)品都時OLE2結(jié)構(gòu)(底層就是2進制)文件,這是office2003以前;然而poi操作大數(shù)據(jù)時,就會有bug,然而jxl也是這種數(shù)據(jù)結(jié)構(gòu),但可以解決這問題,所以jxl當(dāng)時比office厲害。
從office2007開始,微軟就重新開發(fā)了office,底層使用OOXML結(jié)構(gòu),這種數(shù)據(jù)結(jié)構(gòu),可以操作大數(shù)據(jù),所以excel底層就是xml格式文件。
2、(一般導(dǎo)入導(dǎo)出數(shù)據(jù)時用)jxl時僅用來操作excel,并且僅支持2003以下版本,不支持2007,也僅僅時OLE2文檔結(jié)構(gòu)。
二、jar準(zhǔn)備
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
<version>3.9</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>3.9</version>
</dependency>
三、怎么使用(本質(zhì)就是將內(nèi)存中的數(shù)據(jù)通過流的方式寫到硬盤上)
1、訓(xùn)練
第一節(jié)認(rèn)清:excel怎么創(chuàng)建表然后怎么寫內(nèi)容然后再保存文件
創(chuàng)建一個工作簿
創(chuàng)建一個工作表,默認(rèn)是3個工作表
定位哪一個行
定位哪一列
單元格寫內(nèi)容(這前面5步都是在內(nèi)存中進行的,不要被表面迷惑)
點擊保存(這將內(nèi)存中數(shù)據(jù)序列化到硬盤上)
關(guān)閉
第二節(jié):設(shè)置單元格內(nèi)容
Workbook wb =new HSSFWorkbook();
Sheet sh = wb.createSheet();
Row row = sh.createRow(1);
Cell cell = row.createCell(1);
cell.setCellValue("我是中國人");
FileOutputStream op =new FileOutputStream("D:\B.xls");
wb.write(op);執(zhí)行完這一步,內(nèi)容就寫到硬盤上,即B.xls創(chuàng)建了
op.close(); 如果還沒執(zhí)行這一步,那么excel進程提示被占用,不能編輯。關(guān)閉流后,才可以操作excel
第三節(jié):設(shè)置樣式(這和設(shè)置內(nèi)容是不同的東西)--------重點
CellStyle cs=wb.createCellStyle();//注意這方法是工作簿的,單元格樣式,excel無論是合并還是什么的都是單元格,還有邊框,背景色等等都是樣式,所以樣式就是全局的
Font font=wb.createFont();//創(chuàng)建字體,也只能工作簿擁有,是全局的,字體無論在哪一張工作表都是擁有工作簿提供的所有字體,所以這個方法只能是工作簿的
font.setFontName("微軟雅黑");//設(shè)置字體
font.setFontHeightInPoints((short)24);//設(shè)置字體高度值點數(shù),就是設(shè)置大小,點數(shù)相當(dāng)于單位-------注意的地方,也可以用setFontHeight,但單位不同,需要換算,所以統(tǒng)一用setFontHeightInPoints,
cs.setFont(font);設(shè)置字體樣式
cell.setCellStyle(cs);//設(shè)置單元格樣式
創(chuàng)建樣式對象-->創(chuàng)建字體對象-->設(shè)置字體對象各種樣式-->設(shè)置字體-->設(shè)置單元格樣式
第四節(jié):優(yōu)化代碼
為了避免重復(fù)new對象,造成內(nèi)存損失,可以引用利用(引用被利用,沒有被引用指向的對象會被垃圾回收期回收)java是一個引用同時間只能指向一個對象(不然虛擬機不知道調(diào)用那個對象),多個對象可以指向一個引用
A n = new Hero();
n = new Hero();//同一個引用garen指向新創(chuàng)建的對象,上面那個對象就會被垃圾回收機制回收(多個對象可以指向一個引用)
1設(shè)置單元格內(nèi)容再利用:
row1 = sh.createRow(2); 再利用 row引用
cell2 = row1.createCell(2);在利用cell引用
cell2.setCellValue("woshizhongguoren ");
2設(shè)置樣式再利用(因為是全局的,所以會出現(xiàn)后者覆蓋前者,所以不能直接利用font,cs,)
所以必須font 和cs初始化
font= wb.createFont();再利用font引用
cs = wb.createCellStyle();再利用cs引用
font.setFontHeightInPoints((short)18);
font.setFontName("隸書");
font.setBoldweight(Font.BOLDWEIGHT_BOLD);
cs.setFont(font);
cell.setCellStyle(cs);
以上代碼可以抽取出來:
excel 標(biāo)題樣式一般相同,內(nèi)容樣式一般相同,簡化代碼,每次調(diào)用方法前,初始化對象
Workbook wb =new HSSFWorkbook();
Sheet sh = wb.createSheet();
Row row = sh.createRow(1);
Cell cell = row.createCell(1);
cell.setCellValue("我是中國人");
CellStyle cs=wb.createCellStyle();
Font font=wb.createFont();
this.getCellStylefont(font, cs, cell);
初始化:
row = sh.createRow(2);
cell = row.createCell(2);
cell.setCellValue("woshizhongguoren ");
font= wb.createFont();
cs = wb.createCellStyle();
this.getCellStyletext(font, cs, cell);
這是excel標(biāo)題
public CellStyle getCellStyleTitle(Font font,CellStyle cs,Cell cell){ font.setFontHeightInPoints((short)18); font.setFontName("隸書"); font.setBoldweight(Font.BOLDWEIGHT_BOLD); cs.setFont(font); cell.setCellStyle(cs); return cs; }
這是excel 內(nèi)容
public CellStyle getCellStyletext(Font font,CellStyle cs,Cell cell){ font.setFontName("微軟雅黑"); font.setFontHeightInPoints((short)85); cs.setFont(font); cell.setCellStyle(cs); return cs; }
2、項目運用
打印一張出貨表:
日期處理可以數(shù)據(jù)庫處理,也可以在poi處理
加個鏈接,添加打印按鈕,根據(jù)日期,后臺通過sql查詢,返回list集合,通過循環(huán)遍歷導(dǎo)出一張excel表
添加單元格數(shù)據(jù):
List<OutProduct> outProductList = outProductService.find(paraMap);
HSSFWorkbook wb =new HSSFWorkbook();
HSSFSheet sh = wb.createSheet();
int i=1; //定義列起始索引
int j=0;//定義行起始索引
HSSFRow cr ;//聲明行局部變量
HSSFCell cc;//聲明單元格局部變量
String[] arr=new String[]{"客戶","訂單號","貨號","數(shù)量","工廠","工廠交期","船期","貿(mào)易條款"}; //標(biāo)題欄
cr=sh.createRow(j++);
for (String val : arr) {
cc=cr.createCell(i++);
cc.setCellValue(val);
}
for(OutProduct op: outProductList){ //數(shù)據(jù)欄
i=1; //初始化列起始索引
cr=sh.createRow(j++); j++就是j先賦值,然后再自增
cc=cr.createCell(i++);
String customName = op.getCustomName();
cc.setCellValue(customName);
cc=cr.createCell(i++);
String contractNo = op.getContractNo();
cc.setCellValue(contractNo);
cc=cr.createCell(i++);
String productNo = op.getProductNo();
cc.setCellValue(productNo);
cc=cr.createCell(i++);
String boxNum = op.getBoxNum();
cc.setCellValue(boxNum);
cc=cr.createCell(i++);
String factoryName = op.getFactoryName();
cc.setCellValue(factoryName);
cc=cr.createCell(i++);
String deliveryPeriod = op.getDeliveryPeriod();
cc.setCellValue(deliveryPeriod);
cc=cr.createCell(i++);
String shipTime = op.getShipTime();
cc.setCellValue(shipTime);
cc=cr.createCell(i++);
String tradeTerms = op.getTradeTerms();
cc.setCellValue(tradeTerms);
}
OutputStream out =new FileOutputStream("d://a.xls");
wb.write(out);
out.close();
這里可以看出j++好處,中間插入一個大標(biāo)題,下面的內(nèi)容自動往下移動
這部分仍是添加單元格內(nèi)容:大標(biāo)題是合并單元格,
所以表格的合并單元格方法,指定4個參數(shù),起始行,結(jié)束行,起始列,結(jié)束列
然后怎么做到“2018年8月份出貨表”,時間是動態(tài)的,前臺傳過來的,
replaceFirst成功則返回替換的字符串,失敗則返回原始字符串,所以要先進行-0替換,大范圍替換,沒有0則返回原始字符串,然后小范圍-替換成年
sh.addMergedRegion(new CellRangeAddress(0, 0, 1, 8)); //合并單元格默認(rèn)是設(shè)置在區(qū)域的第一行第一列
cr=sh.createRow(j++);
cc=cr.createCell(1);
cc.setCellValue(pdate.replaceFirst("-0", "年").replaceFirst("-", "年")+"月份出貨表");
添加單元格樣式:
設(shè)置字體和樣式:
這兩個對象都是workbook對象創(chuàng)建的,字體包含在樣式中
HSSFCellStyle nstyle = wb.createCellStyle();
HSSFFont nfont = wb.createFont();
bigTitle(wb, nstyle, nfont);
cc.setCellStyle(nstyle);
行高是行對象創(chuàng)建,
cr.setHeightInPoints(36);
字體加粗是字體對象
單元格內(nèi)容居中這是樣式對象,樣式CellStyle中有橫向和豎向常量
public void bigTitle(Workbook wb,CellStyle nstyle,Font nfont){
nfont.setFontHeightInPoints((short)16);
nfont.setFontName("宋體");
nstyle.setFont(nfont);
nfont.setBoldweight(HSSFFont.BOLDWEIGHT_BOLD);//加粗,這是常量,是HSSFFont類中的常量
nstyle.setAlignment(CellStyle.ALIGN_CENTER);橫向居中
nstyle.setVerticalAlignment(CellStyle.VERTICAL_CENTER);縱向居中
}
設(shè)置邊框 nstyle.setBorderBottom(CellStyle.BORDER_THIN); nstyle.setBorderLeft(CellStyle.BORDER_THIN); nstyle.setBorderRight(CellStyle.BORDER_THIN); nstyle.setBorderTop(CellStyle.BORDER_THIN);
分大標(biāo)題 標(biāo)題 正文設(shè)置樣式,每次設(shè)置樣式錢需要初始化,
初始化 nstyle = wb.createCellStyle(); nfont = wb.createFont();
public void bigTitle(Workbook wb,CellStyle nstyle,Font nfont){
nfont.setFontHeightInPoints((short)16);
nfont.setFontName("宋體");
nfont.setBoldweight(HSSFFont.BOLDWEIGHT_BOLD);
nstyle.setAlignment(CellStyle.ALIGN_CENTER);
nstyle.setVerticalAlignment(CellStyle.VERTICAL_CENTER);
nstyle.setFont(nfont);
}
public CellStyle title(Workbook wb,CellStyle nstyle,Font nfont){
nfont.setFontHeightInPoints((short)12);
nfont.setFontName("黑體");
nstyle.setAlignment(CellStyle.ALIGN_CENTER);
nstyle.setVerticalAlignment(CellStyle.VERTICAL_CENTER);
nstyle.setBorderBottom(CellStyle.BORDER_THIN);
nstyle.setBorderLeft(CellStyle.BORDER_THIN);
nstyle.setBorderRight(CellStyle.BORDER_THIN);
nstyle.setBorderTop(CellStyle.BORDER_THIN);
nstyle.setFont(nfont);
return nstyle;
}
public CellStyle text(Workbook wb,CellStyle nstyle,Font nfont){
nfont.setFontHeightInPoints((short)10);
nfont.setFontName("Times New Roman");
nstyle.setAlignment(CellStyle.ALIGN_LEFT);
nstyle.setVerticalAlignment(CellStyle.VERTICAL_CENTER);
nstyle.setFont(nfont);
return nstyle;
}
列寬特殊,這是表對象的,sheet:通過源碼可以得出,需要乘以256,這是api bug
然而還是差那么一點點,這是poi一個bug,不能夠精確。所以經(jīng)過測試一般接近準(zhǔn)確*300,
a列是為了裝訂線的位置
打印時,不能在一頁顯示,可以設(shè)置橫向,拖到一頁,可以設(shè)置頁面方向
設(shè)置頁眉頁腳,設(shè)置重復(fù)標(biāo)題行
上面所說的都是下載,用戶體驗不好,項目中都是模板開發(fā),用戶需求變更,只要改變模板上的樣式,并且模板的樣式都是通過excel手動設(shè)置,不需要在代碼中設(shè)置,代碼中只要獲取一行模板樣式,后面的內(nèi)容全設(shè)置成模板樣式就可以了,
可以解決上面所有問題。不然每次輸出的文件在服務(wù)器端,用戶沒法看,
項目上傳下載 用工具類
ByteArrayOutputStream bo=new ByteArrayOutputStream();
wb.write(bo);
DownloadUtil du=new DownloadUtil();
du.download(bo, response, "出貨表.xls");
工具類: * @param byteArrayOutputStream 將文件內(nèi)容寫入ByteArrayOutputStream
* @param response HttpServletResponse 寫入response
* @param returnName 返回的文件名
*/
public void download(ByteArrayOutputStream byteArrayOutputStream, HttpServletResponse response, String returnName) throws IOException{
response.setContentType("application/octet-stream;charset=utf-8");
returnName = response.encodeURL(new String(returnName.getBytes(),"iso8859-1")); //保存的文件名,必須和頁面編碼一致,否則亂碼
response.addHeader("Content-Disposition", "attachment;filename=" + returnName);
response.setContentLength(byteArrayOutputStream.size());
ServletOutputStream outputstream = response.getOutputStream(); //取得輸出流
byteArrayOutputStream.writeTo(outputstream); //寫到輸出流
byteArrayOutputStream.close(); //關(guān)閉
outputstream.flush(); //刷數(shù)據(jù)
}
通過模板開發(fā):下面是模板
主要步驟:讀取服務(wù)器上的模板文件到內(nèi)存中,然后在內(nèi)存中對內(nèi)存中的模板進行大標(biāo)題:設(shè)置動態(tài)值,標(biāo)題:不用管了,用內(nèi)存中模板標(biāo)題,內(nèi)容上:設(shè)置內(nèi)容、樣式和模板一行內(nèi)容樣式一樣
@RequestMapping("/cargo/outproduct/outProductPrint.action")
public void print(String inputDate, HttpServletResponse response) throws FileNotFoundException, IOException, ParseException{
/*
* 操作步驟:
* 1、獲取數(shù)據(jù)
* 2、POI寫數(shù)據(jù)到文件
*/
List<OutProduct> oList = outProductService.findOutProduct(inputDate+"%");
Workbook wb = new HSSFWorkbook(new FileInputStream(new File("c:\tFACTORY.xls"))); //打開模板文件
Sheet sheet = wb.getSheetAt(0); //打開第一個工作表
Row nRow = null;
Cell nCell = null;
int rowNo = 2; //行號
int colNo = 1; //列號
//處理標(biāo)題
nRow = sheet.getRow(0); //獲得行對象
nCell = nRow.getCell(1); //獲得單元格對象
nCell.setCellValue(inputDate.replaceFirst("-0", "-").replaceFirst("-", "年")+"月份出貨表"); //yyyy-MM 2010-08
//獲取模板文件中的樣式
nRow = sheet.getRow(2);
nCell = nRow.getCell(1);
CellStyle customNameStyle = nCell.getCellStyle(); //獲取客戶名稱樣式
nRow = sheet.getRow(2);
nCell = nRow.getCell(2);
CellStyle contractNoStyle = nCell.getCellStyle();
nRow = sheet.getRow(2);
nCell = nRow.getCell(3);
CellStyle productNoStyle = nCell.getCellStyle();
nRow = sheet.getRow(2);
nCell = nRow.getCell(4);
CellStyle cnumberStyle = nCell.getCellStyle();
nRow = sheet.getRow(2);
nCell = nRow.getCell(5);
CellStyle factoryStyle = nCell.getCellStyle();
nRow = sheet.getRow(2);
nCell = nRow.getCell(6);
CellStyle extStyle = nCell.getCellStyle();
nRow = sheet.getRow(2);
nCell = nRow.getCell(7);
CellStyle dateStyle = nCell.getCellStyle();
nRow = sheet.getRow(2);
nCell = nRow.getCell(9);
CellStyle tradeTermsStyle = nCell.getCellStyle();
for(int i=0;i<oList.size();i++){
colNo = 1;
OutProduct op = oList.get(i); //獲取每個出貨表對象
nRow = sheet.createRow(rowNo++); //創(chuàng)建行
nRow.setHeightInPoints(24); //行高
nCell = nRow.createCell(colNo++); //創(chuàng)建單元格
nCell.setCellValue(op.getCustomName());
nCell.setCellStyle(customNameStyle);
nCell = nRow.createCell(colNo++);
nCell.setCellValue(op.getContractNo());
nCell.setCellStyle(contractNoStyle);
nCell = nRow.createCell(colNo++);
nCell.setCellValue(op.getProductNo());
nCell.setCellStyle(productNoStyle);
nCell = nRow.createCell(colNo++);
nCell.setCellValue(op.getCnumber());
nCell.setCellStyle(cnumberStyle);
nCell = nRow.createCell(colNo++);
nCell.setCellValue(op.getFactoryName());
nCell.setCellStyle(factoryStyle);
nCell = nRow.createCell(colNo++);
nCell.setCellValue("附件");
List<String> extNameList = outProductService.getExtName(op.getContractProductId());
String _extName = "";
if(extNameList!=null&&extNameList.size()>0){
for(String extName : extNameList){
_extName += extName + "
"; //換行符
}
_extName = _extName.substring(0,_extName.length()-1); //去掉最后一個字符
}else{
_extName = "無";
}
nCell.setCellValue(_extName);
nCell.setCellStyle(extStyle);
nCell = nRow.createCell(colNo++);
//nCell.setCellValue(UtilFuns.dateTimeFormat(op.getDeliveryPeriod())); //利用工具類轉(zhuǎn)類型,同時進行格式化
nCell.setCellValue(op.getDeliveryPeriod());
nCell.setCellStyle(dateStyle);
nCell = nRow.createCell(colNo++);
//nCell.setCellValue(UtilFuns.dateTimeFormat(op.getShipTime()));
nCell.setCellValue(op.getShipTime());
nCell.setCellStyle(dateStyle);
nCell = nRow.createCell(colNo++);
nCell.setCellValue(op.getTradeTerms());
nCell.setCellStyle(tradeTermsStyle);
}
DownloadUtil du = new DownloadUtil();
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); //生成流對象
wb.write(byteArrayOutputStream);
du.download(byteArrayOutputStream, response, "出貨表.xls"); //彈出下載框,用戶就可以直接下載
}
下面是導(dǎo)出下載后的結(jié)果:
String path=request.getSession().getServletContext().getRealPath("/")+"/make/xlsprint";//必須是/的虛擬路徑,不然jdk1.8不會拼接/后面的字符串 ,這紅色的斜杠最好加上,多個斜杠,底層會處理,不要緊
InputStream is =new FileInputStream(new File(path+"/tOUTPRODUCT.xls"));//這里紅色/也一樣,不加也可以
HSSFWorkbook wb =new HSSFWorkbook(is);
總結(jié)
- 上一篇: 晚上吃虾会胖吗
- 下一篇: iOS开发之主题皮肤