項目需要,要實現(xiàn)一個導入導出excel的功能扑馁,于是,任務驅(qū)動著我學習到了POI和JXL這2個java操作Excel的插件凉驻。
一腻要、POI和JXL介紹
1、POI:是對所有office資源進行讀寫的一套工具包涝登、屬于apache開源組織雄家。
poi操作excel:
poi是把整個文件的屬性都封裝在HSSFWorkbook 中;
通過HSSFWorkbook來操作單個工作薄。然后通過工作薄來操作行;
在通過行來操控單元格胀滚。這樣一級一級的分拆下來;
HSSFWorkbook---->HSSFSheet----->HSSFRow---->HSSFCell;
由于是基于HSSFWorkbook對象一步步創(chuàng)建起來的趟济。所以不用把創(chuàng)建好的單元格添加進這個對象中、
如果需要對部分表格進行設置樣式什么的蛛淋。就可以創(chuàng)立HSSFCellStyle對象來進行設定樣式;
2咙好、JXL:只能對excel進行操作的一套工具包。
jxl是把整個文件封裝在Workbook相關對象中;
通過Workbook去創(chuàng)建sheet工作薄;但是和poi不一樣的地方是
jxl是通過向sheet中使用label(單元格)來進行讀取寫入;
Workbook----->sheet------>label ;
jxl是先創(chuàng)建一個工作區(qū)域褐荷、然后區(qū)創(chuàng)立單元格勾效、單元格包含這個單元格的位置、內(nèi)容等信息;然后把這個單元格加入工作區(qū);
二、實例操作
1层宫、用POI方式實現(xiàn)導出數(shù)據(jù)到excel功能
實現(xiàn)思路:當一點擊導出按鈕就跳轉(zhuǎn)到指定的action去執(zhí)行對應方法杨伙,先調(diào)用dao對數(shù)據(jù)庫數(shù)據(jù)進行查詢,根據(jù)dao層的方法返回一個集合對象萌腿,然后把這個集合對象的數(shù)據(jù)交互給POI去動態(tài)生成單元格并設置進去限匣。項目后端我用的是ssh框架搭建項目,所以邏輯代碼我寫在action類中毁菱。
直接看以下代碼示例
前端jsp頁面按鈕部分
<button type="button" class="btn btn-default" style="width:80px;height:30px">
<a href="excelAction!exportExchangeSudent"><i class="icon_upload"></i> <b>導出</b></a>
</button>
交換生實體類
package com.international.model;
import java.util.Date;
import cn.afterturn.easypoi.excel.annotation.Excel;
import cn.afterturn.easypoi.excel.annotation.ExcelTarget;
public class ExchangeStudent {
//此表按照單表來操作米死,只用作記錄
private int id;
private String studentNo;
private String studentName;
private String sex;
private String major;
private String className;
private Date startTime;
private Date endTime;
private String exchangeCollege;
private String reserves1;
private String reserves2;
private String reserves3;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getStudentNo() {
return studentNo;
}
public void setStudentNo(String studentNo) {
this.studentNo = studentNo;
}
public String getStudentName() {
return studentName;
}
public void setStudentName(String studentName) {
this.studentName = studentName;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public String getMajor() {
return major;
}
public void setMajor(String major) {
this.major = major;
}
public String getClassName() {
return className;
}
public void setClassName(String className) {
this.className = className;
}
public Date getStartTime() {
return startTime;
}
public void setStartTime(Date startTime) {
this.startTime = startTime;
}
public Date getEndTime() {
return endTime;
}
public void setEndTime(Date endTime) {
this.endTime = endTime;
}
public String getExchangeCollege() {
return exchangeCollege;
}
public void setExchangeCollege(String exchangeCollege) {
this.exchangeCollege = exchangeCollege;
}
public String getReserves1() {
return reserves1;
}
public void setReserves1(String reserves1) {
this.reserves1 = reserves1;
}
public String getReserves2() {
return reserves2;
}
public void setReserves2(String reserves2) {
this.reserves2 = reserves2;
}
public String getReserves3() {
return reserves3;
}
public void setReserves3(String reserves3) {
this.reserves3 = reserves3;
}
}
后端action類內(nèi)的業(yè)務方法代碼
//導出交換生
public String exportExchangeSudent() throws Exception{
//獲取交換生對象
List<ExchangeStudent> studentList = esd.queryInterStudents("");
String []tableHeader={"學號","姓名","性別","班級","專業(yè)","交換開始時間","交換結(jié)束時間","交換的院校"};
short cellNumber=(short)tableHeader.length;//表的列數(shù)
workbook = new HSSFWorkbook(); //創(chuàng)建一個Excel
style = workbook.createCellStyle(); //設置表頭的類型
style.setAlignment(HorizontalAlignment.CENTER);
style1 = workbook.createCellStyle(); //設置數(shù)據(jù)類型
style1.setAlignment(HorizontalAlignment.CENTER);
HSSFFont font = workbook.createFont(); //設置字體
HSSFSheet sheet = workbook.createSheet("sheet1"); //創(chuàng)建一個sheet
HSSFHeader header = sheet.getHeader();//設置sheet的頭
try {
//根據(jù)是否取出數(shù)據(jù),設置header信息
if(studentList.size() < 1 ){
header.setCenter("查無資料");
}else{
header.setCenter("交換生表");
row = sheet.createRow(0);
row.setHeight((short)400);
//表頭
for(int k = 0;k < cellNumber;k++){
cell = row.createCell((short) k);//創(chuàng)建第0行第k列
cell.setCellValue(tableHeader[k]);//設置第0行第k列的值
sheet.setColumnWidth((short)k,(short)8000);//設置列的寬度
font.setColor(HSSFFont.COLOR_NORMAL); // 設置單元格字體的顏色.
font.setFontHeight((short)350); //設置單元字體高度
style1.setFont(font);//設置字體風格
cell.setCellStyle(style1);
}
// 給Excel填充數(shù)據(jù)
for(int i = 0 ;i < studentList.size() ;i++){
//獲取InternationalStudent對象
ExchangeStudent student1 = (ExchangeStudent)studentList.get(i);
row = sheet.createRow((short) (i + 1));//創(chuàng)建第i+1行
row.setHeight((short)400);//設置行高
if(student1.getStudentNo() != null){
cell = row.createCell((short) 0);//創(chuàng)建第i+1行第0列
cell.setCellValue(student1.getStudentNo());//設置第i+1行第0列的值
cell.setCellStyle(style);//設置風格
}
if(student1.getStudentName() != null){
cell = row.createCell((short) 1); //創(chuàng)建第i+1行第1列
cell.setCellValue(student1.getStudentName());//設置第i+1行第1列的值
cell.setCellStyle(style); //設置風格
}
if(student1.getSex() != null){
cell = row.createCell((short) 2);
cell.setCellValue(student1.getSex());
cell.setCellStyle(style);
}
if(student1.getClassName()!= null){
cell = row.createCell((short) 3);
cell.setCellValue(student1.getClassName());
cell.setCellStyle(style);
}
if(student1.getMajor()!= null){
cell = row.createCell((short) 4);
cell.setCellValue(student1.getMajor());
cell.setCellStyle(style);
}
if(student1.getStartTime() != null){
cell = row.createCell((short) 5);
cell.setCellValue(student1.getStartTime().toString().substring(0,10));
cell.setCellStyle(style);
}
if(student1.getEndTime() != null){
cell = row.createCell((short) 6);
cell.setCellValue(student1.getEndTime().toString().substring(0,10));
cell.setCellStyle(style);
}
if(student1.getExchangeCollege() != null){
cell = row.createCell((short) 7);
cell.setCellValue(student1.getExchangeCollege());
cell.setCellStyle(style);
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
outputSetting("交換生表.xls");
return null;
}
//固定配置
public void outputSetting(String fileName) {
HttpServletResponse response = null;//創(chuàng)建一個HttpServletResponse對象
OutputStream out = null;//創(chuàng)建一個輸出流對象
try {
response = ServletActionContext.getResponse();//初始化HttpServletResponse對象
out = response.getOutputStream();// 得到輸出流
response.setHeader("Content-disposition","attachment; filename="+new String(fileName.getBytes(),"ISO-8859-1"));//filename是下載的xls的名
response.setContentType("application/msexcel;charset=UTF-8");//設置類型
response.setHeader("Pragma","No-cache");//設置頭
response.setHeader("Cache-Control","no-cache");//設置頭
response.setDateHeader("Expires", 0);//設置日期頭
workbook.write(out);
out.flush();
workbook.write(out);
} catch (IOException e) {
e.printStackTrace();
}finally{
try{
if(out!=null){
out.close();
}
}catch(IOException e){
e.printStackTrace();
}
}
}
一些關于ssh框架的配置代碼
//struts.xml文件
<!-- 導出Excel的Action -->
<action name="excelAction" class="excelAction">
</action>
//applicationContext-actions.xml
<!-- 導出的bean -->
<bean id="excelAction" class="com.international.common.ExcelAction" scope="prototype">
<property name="esd">
<ref bean="exchangeStuDao"/>
</property>
</bean>
<!-- 交換生的bean -->
<bean id="exchangeStudentAction" class="com.international.actions.student.ExchangeStudentAction">
<property name="esd">
<ref bean="exchangeStuDao"/>
</property>
</bean>
編寫完以上代碼贮庞,通過poi方式導出excel數(shù)據(jù)就實現(xiàn)了峦筒,主要關注action類那部分代碼。
2窗慎、用JXL方式實現(xiàn)導入excel數(shù)據(jù)功能
實現(xiàn)思路:前端界面按下導入按鈕物喷,彈出個模態(tài)框,提示選擇導入excel文件遮斥,其實導入功能是先上傳文件到服務器指定路徑峦失,然后通過JXL去讀取上傳的excel數(shù)據(jù),然后裝進集合對象里术吗,最后再循環(huán)的把集合對象里的數(shù)據(jù)遍歷插入到數(shù)據(jù)庫中尉辑,相當于遍歷調(diào)用添加交換生的方法牛哺。
直接看下面貼出來的代碼耀鸦。
前端jsp頁面關鍵代碼,有用到bootstrap框架
<div class="row" style="margin:5px">
<div class="nav search-row" id="top_menu">
<!-- search form start -->
<ul class="nav top-menu">
<li>
<form class="navbar-form" action="exchangeStudentAction!showStudent" method="post">
<input class="form-control" name="loginUserName" placeholder="輸入查找的關鍵字" type="text" required/>
<button type="submit" class="btn btn-default" style="width:80px;height:30px">
<i class=" icon_search"></i> <b>搜索</b></button>
<button type="button" class="btn btn-default" style="width:80px;height:30px">
<a href="addExchangeStudent.jsp"><i class="icon_plus_alt2"></i> <b>添加</b></a></button>
<button type="button" class="btn btn-default" style="width:120px;height:30px">
<a href="exchangeStudentAction!showStudent?pageNo=1"><i class="icon_menu"></i> <b>顯示全部</b></a></button>
<button type="button" class="btn btn-default" style="width:80px;height:30px">
<a href="excelAction!exportExchangeSudent"><i class="icon_upload"></i> <b>導出</b></a>
</button>
<button class="btn btn-default" data-toggle="modal" data-target="#myModal" style="width:80px;height:30px; float:rigtht">
<a href="#"><i class="icon_download"></i> <b>導入</b></a>
</button>
<a class="btn btn-default" href="excelAction!downloadExStudent" style="width:140px;height:30px"><i class="icon_upload"></i> <b>下載Excel模板</b></a>
</form>
</li>
</ul>
<!-- search form end -->
</div>
</div>
<!-- 模態(tài)框(Modal) -->
<div class="modal fade" id="myModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">
×
</button>
<h4 class="modal-title" id="myModalLabel">
導入交換生EXCEL表
</h4>
</div>
<div class="modal-body">
<form enctype="multipart/form-data" id="studentExcel" method="post">
<input id="file-zh" name="upload" type="file" multiple>
<br/><br/>
<b><a class="btn btn-primary">導入前請先下載交換生Excel表模板,按規(guī)范導入</a></b>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">關閉
</button>
<button type="button" class="btn btn-primary" data-dismiss="modal" onclick="importExcel()">
提交
</button>
</div>
</div><!-- /.modal-content -->
</div><!-- /.modal -->
</div>
js代碼實現(xiàn)跳轉(zhuǎn)action
//導入excel文件
function importExcel(){
var formData = new FormData($("#studentExcel")[0]); // 要求使用的html對象
//console.log(formData);
$.ajax(
{
type:"post",
url:"http://localhost:8080/InternationalSys/background/importExcelAction!importExStudentExcel",
//注:如果沒有文件吝镣,只是簡單的表單數(shù)據(jù)則可以使用 $('#formid').serialize();
data:formData,
dataType:"json",
async:false,
contentType: false,
processData: false,
success: function(data){
if(data!=null && data!=""){
alert(data);
}
}
}
);
}
</script>
后端action類內(nèi)的邏輯方法代碼
package com.international.common;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang.StringUtils;
import jxl.*;
import org.apache.struts2.ServletActionContext;
import com.international.dao.ExchangeStuDao;
import com.international.model.ExchangeStudent;
import com.opensymphony.xwork2.ActionSupport;
import jxl.read.biff.BiffException;
import jxl.write.DateTime;
public class ImportExcelAction extends ActionSupport{
private String uploadFileName;
private File upload;
private String savePath;
private List<ExchangeStudent> exStudentList = new ArrayList<ExchangeStudent>();
private ExchangeStuDao esd;
public ExchangeStuDao getEsd() {
return esd;
}
public void setEsd(ExchangeStuDao esd) {
this.esd = esd;
}
public String getUploadFileName() {
return uploadFileName;
}
public void setUploadFileName(String uploadFileName) {
this.uploadFileName = uploadFileName;
}
public File getUpload() {
return upload;
}
public void setUpload(File upload) {
this.upload = upload;
}
public String getSavePath() {
return savePath;
}
public void setSavePath(String savePath) {
this.savePath = savePath;
}
public List<ExchangeStudent> getExStudentList() {
return exStudentList;
}
public void setExStudentList(List<ExchangeStudent> exStudentList) {
this.exStudentList = exStudentList;
}
//導入excel數(shù)據(jù)到交換生表
@SuppressWarnings("static-access")
public void importExStudentExcel() throws IOException{
System.out.println("excel的值為:"+ uploadFileName);
String message="";
String path=null;
Workbook book = null; //jxl工作簿
InputStream fileIn = null;
if(uploadFileName!=null && !uploadFileName.equals("")){
//攔截僅允許上傳文件類型
if(uploadFileName.substring(uploadFileName.lastIndexOf(".")).equals(".xls")) {
//獲取需要上傳文件的文件路徑
path=ServletActionContext.getServletContext().getRealPath(this.getSavePath()+ "\\" +this.uploadFileName);
System.out.println("上傳Excel的路徑:"+path);
//判斷文件是否上傳,如果上傳的話將會創(chuàng)建該目錄
File target= new File(path); // 定義目標文件對象
try {
FileUtils.copyFile(upload, target);
} catch (Exception e) {
e.printStackTrace();
}
// 刪除臨時文件
upload.delete();
int sum=0;//計算導入成功的條數(shù)
try {
System.out.println("上傳路徑:"+path);
fileIn = new FileInputStream(path);
//根據(jù)指定的文件輸入流導入Excel從而產(chǎn)生Workbook對象
System.out.println("輸入流:"+fileIn);
//獲取Excel對象
try {
book = book.getWorkbook(new File(path));
} catch (BiffException e1) {
// TODO 自動生成的 catch 塊
e1.printStackTrace();
}
//獲取Excel的第一個sheet表
Sheet sheet = (Sheet) book.getSheet(0);
System.out.println("獲取到的Excel數(shù)據(jù):"+ sheet);
System.out.println("行數(shù):"+sheet.getRows());
System.out.println("列數(shù):"+sheet.getColumns());
//對Sheet中的每一行進行迭代
for (int i = 1; i < sheet.getRows(); i++) {
//創(chuàng)建實體類
ExchangeStudent exStudent = new ExchangeStudent();
// 獲取第一列第二行單元格對象
exStudent.setStudentNo(sheet.getCell(0, i).getContents());
exStudent.setStudentName(sheet.getCell(1, i).getContents());
exStudent.setSex(sheet.getCell(2, i).getContents());
exStudent.setClassName(sheet.getCell(3, i).getContents());
exStudent.setMajor(sheet.getCell(4, i).getContents());
Date sTime = null;
//日期格式處理方式:
if(sheet.getCell(5, i).getType() == CellType.DATE){
DateCell dc = (DateCell)sheet.getCell(5, i);
sTime = dc.getDate(); //獲取單元格的date類型
}
exStudent.setStartTime(sTime);
Date eTime = null;
//日期格式處理方式:
if(sheet.getCell(6, i).getType() == CellType.DATE){
DateCell dc = (DateCell)sheet.getCell(6, i);
eTime = dc.getDate(); //獲取單元格的date類型
}
exStudent.setEndTime(eTime);
exStudent.setExchangeCollege(sheet.getCell(7, i).getContents());
//不是空對象才加入集合中
if(exStudent!=null) {
exStudentList.add(exStudent);
}
}
System.out.println(exStudentList);
for(int i=0; i<exStudentList.size(); i++){
if(!esd.queryStudentNo(exStudentList.get(i).getStudentNo())) {
if(esd.addExStudent(exStudentList.get(i))){
sum++;
}
}else {
message = "此條學生記錄已存在";
System.out.println("此條學生記錄已存在");
continue;
}
}
System.out.println("導入的學生人數(shù):"+sum);
if(sum>0){
System.out.println("導入成功");
message="導入成功";
}else{
System.out.println("導入失敗");
message="導入失敗";
}
fileIn.close();
} catch (IOException ex) {
ex.printStackTrace();
}
}else {
System.out.println("上傳的只能是后綴為.xls的Excel文件");
message = "上傳的只能是后綴為.xls的Excel文件";
}
}else{
message="請先上傳交換生信息的Excel文件";
System.out.println("請先上傳交換生信息的Excel文件");
}
ajaxAction.toJson(ServletActionContext.getResponse(),message);
}
}
dao層的代碼
//根據(jù)學號查詢數(shù)據(jù)庫是否有這個交換生
public boolean queryStudentNo(String stuNo) {
Session session = null;
try {
session = sessionFactory.openSession();
String hql = "from ExchangeStudent where studentNo="+stuNo;
Query query = session.createQuery(hql);
List list = query.list();
if(list.size()>0) {
return true;
}else {
return false;
}
}catch (Exception e) {
e.printStackTrace();
return false;
}finally {
session.close();
}
}
//添加交換生信息
public boolean addExStudent(ExchangeStudent exchangeStudent) {
Session session = null;
try{
session = sessionFactory.openSession();
Transaction tran = session.beginTransaction();
session.save(exchangeStudent);
tran.commit();
return true;
}catch(Exception e){
e.printStackTrace();
return false;
}finally{
session.close();
}
}
一些ssh的配置文件
//applicationContext-actions.xml
<!-- 導入的bean -->
<bean id="importExcelAction" class="com.international.common.ImportExcelAction">
<property name="esd">
<ref bean="exchangeStuDao"/>
</property>
</bean>
//struts.xml
<!-- 導入Excel的Action -->
<action name="importExcelAction" class="importExcelAction">
<param name="savePath">/upload</param>
</action>
根據(jù)以上的代碼昆庇,通過jxl方法的導入功能也實現(xiàn)了末贾,主要關注action類的那部分代碼。
三整吆、期間遭遇問題(PS:我認為這才是我寫這篇文章的初心和這篇文章的重點拱撵,差不多的功能對于每個人都可能出現(xiàn)許多不同的問題,我們要學會自己解決問題表蝙,我把這分享出來拴测。)
注意:以上導入導出的實例代碼要成功,都得基于導入相應的POI和JXL的jar包府蛇,否則是空談集索。
- 1、關于poi3.17版本設置單元格樣式居中字段
- 2、
報錯:Caused by: com.microsoft.sqlserver.jdbc.SQLServerException: 違反了 UNIQUE KEY 約束“UQ__Exchange__4D119D59097C43E3”务荆。不能在對象“dbo.ExchangeStudent”中插入重復鍵妆距。重復鍵值為 ()。
問題原因:因為我數(shù)據(jù)庫表中studentNo這個字段設置了唯一性約束函匕,所以當excel插入的時候娱据,數(shù)據(jù)庫里面已經(jīng)存在了該條記錄,所以不讓插入盅惜。
解決方案:我在dao層里加入一個方法中剩,就是上面的queryByStudentNo()
方法,用來根據(jù)excel表里的學號數(shù)據(jù)去數(shù)據(jù)庫查詢有無這個學生抒寂,有的話就不插入了咽安,繼續(xù)下一次循環(huán)。 - 3蓬推、導入excel表中的日期數(shù)據(jù)
報錯:java.text.ParseException: Unparseable date: ""
解決參考這里
//思路是借用下面這一段的妆棒,我自己寫的在上面實例代碼。
DateTime accessTime = null;
//日期格式處理方式:
if(sheet.getCell(2, j).getType() == CellType.DATE){
DateCell dc = (DateCell)sheet.getCell(2, j);
Date date = dc.getDate(); //獲取單元格的date類型
accessTime = new DateTime(date);
}
- 4沸伏、JXL讀取Excel文件糕珊,調(diào)用sheet.getRows()竟然返回多余的行數(shù)。這個有點玄學毅糟,明明excel數(shù)據(jù)有3行數(shù)據(jù)而已红选,在程序獲取的時候竟然顯示有33行數(shù)據(jù),最后還是莫名其妙的就恢復正常了姆另。
- 5喇肋、
報錯:jxl讀取excel文件異常,Unable to recognize OLE stream迹辐。
這是jxl導入excel數(shù)據(jù)只支持后綴為.xls文件的問題蝶防。解決方案這里 - 6、ssh上傳文件到服務器隔一會文件會自動刪除明吩。解決方案這里
- 7间学、導入時遇到多表關聯(lián)的數(shù)據(jù),比如我在導入國際班學生的excel數(shù)據(jù)時印荔,excel表里有班級的字段低葫,
報錯:org.hibernate.TransientObjectException: object references an unsaved tran。
經(jīng)排查仍律,問題代碼鎖定在下圖這塊嘿悬。
圖片.png圖片.png
總結(jié):這2功能我花了2天時間葱她,第一次接觸撩扒,期間遇到各種詭異問題,現(xiàn)在總算是實現(xiàn)了吨些,代碼路上搓谆,不放棄才能熬到最后。