先來回顧下通常把java對Excel的操作分為以下功能:1、生成模板,導出模板;2、填充模板,錄入數據;3;讀取數據庫數據,導出數據;在上一篇博文中,我簡單記錄了模板生成和導出,在這篇博文中,主要來記錄--Excel文件導入,數據錄入(仍然是以jsp+servlet為例)
既然要解決這個問題,那首先來分析下我們需要面對的有哪些需求需要實現:
1、Excel文件導入(這是最基礎的,巧婦難為無米之炊,導入環節也是查了好久才完成的);
2、Excel文件中數據的格式判定,你要讀取文件,如果文件中其實沒有數據怎么辦,程序會報錯么;
3、Excel文件中數據的數值判定,“編號”啊,“身份證號”啊之類是不是需要有一定的規范才說明數值是正確的,否則錄入毫無意義,數據庫中可能還會報錯;
4、Excel文件中數據判定完成后,判定全部正確的,全部錄入數據庫,錄入成功顯示錄入成功,錄入不成功則修改錄入環節;
判定如果存在錯誤,將所有出現錯誤的列匯總,返回界面提示所有出錯的列;
首先,我們來完成導入功能,實現如下效果:
相信這個上傳效果,很多人都能實現,<input type="file">如是而已嘛,但是實現它后我們如何進行excel數據操作呢?通常我們想到的有如下兩種方法:
1、將excel文件傳上去,然后對傳上的文件進行操作,因為我們傳到哪了我們知道,可以直接獲取路徑;
2、我們可以直接獲取想要上傳的文件在電腦上的路徑,然后我們通過路徑直接對文件進行操作;
這里我主要來介紹下我實現的第二種方法,(以ie瀏覽器為例,其它瀏覽器暫不討論)
1 function upLoad(){ 2 var myFile=document.getElementById("myFile"); 3 myFile.select(); 4 var realPath=document.selection.createRange().text; 5 var len=realPath.length; 6 var path=realPath.substr(len-4); 7 if(path==".xls"){
document.getElementById("myForm").action="upLoad?path="+realPath; 8 document.getElementById("myForm").submit(); 9 }else{ 10 alert("請輸入excel格式的文件"); 11 } 12 }
通常情況下,在ie7之前我們可以通過document.getElementById('file_upl').value 直接獲取文本的本地路徑,但是在ie8之后,處於安全性考慮,上傳時獲取以上value值則會以“C:\fakepath\”來代替了,這個時候我們就需要 上文中出現的var myFile=document.getElementById("myFile"); myFile.select();var realPath=document.selection.createRange().text;這三步來進行完成了。
這樣在ie瀏覽器下就可以獲取到文件的本地路徑了,從而繼續接下來的文本讀取工作。
其次,我們來解決接下來的問題,既然找到文件了,那就開始對文件進行操作,
(這里我們只考慮,對某個單個文件的直接讀取,不考慮多文件優化等問題)我們通過如下步驟來進行數據讀取:
a、java對excel的數據讀取,原理上是將excel上的數據復制到hssfworkbook工作簿上,然后對工作簿進行操作
1 File file=new FileInputStream(String path) 2 BufferedInputStream bis=new BufferedInputStream(file); 3 POIFSFileSystem pfs=new POIFSFileSystem(bis);
b、復制完成后就是對hssfworkbook工作簿上數據的處理,是從頁、行、列依次進行開始讀取
1 //每一頁每一頁的進行數據處理 2 HSSFWorkbook book=new HSSFWorkbook(pfs); 3 for (int k = 0; k < book.getNumberOfSheets(); k++) { 4 HSSFSheet sheet=book.createSheet(); 5 //每一行每一行的進行處理 6 for (int i = 0; i < sheet.getLastRowNum(); i++) { 7 HSSFRow row=sheet.createRow(i); 8 if(row==null){continue;} 9 String[] values = new String[i]; 10 Arrays.fill(values, ""); 11 for (int j = 0; j < row.getLastCellNum()+1; j++) { 12 String value=""; 13 HSSFCell cell=row.createCell(j);
c、在進行每一個單元格讀取的時候,我們需要根據不同的數據,用不同的獲取方法,避免讀取過程出現異常
1 switch (cell.getCellType()) { 2 case HSSFCell.CELL_TYPE_STRING: 3 value = cell.getStringCellValue(); 4 break; 5 case HSSFCell.CELL_TYPE_NUMERIC: 6 if (HSSFDateUtil.isCellDateFormatted(cell)) { 7 Date date = cell.getDateCellValue(); 8 if (date != null) { 9 value = new SimpleDateFormat("yyyy-MM-dd").format(date); 10 } else { 11 value = ""; 12 } 13 } else { 14 value = newDecimalFormat("0").format(cell.getNumericCellValue()); 15 } 16 break; 17 case HSSFCell.CELL_TYPE_FORMULA: 18 // 導入時如果為公式生成的數據則無值 19 if (!cell.getStringCellValue().equals("")) { 20 value = cell.getStringCellValue(); 21 } else { 22 value = cell.getNumericCellValue() + ""; 23 } 24 break; 25 case HSSFCell.CELL_TYPE_BLANK: 26 value=""; 27 break; 28 case HSSFCell.CELL_TYPE_ERROR: 29 value = ""; 30 break; 31 case HSSFCell.CELL_TYPE_BOOLEAN: 32 value = (cell.getBooleanCellValue() == true ? "Y":"N"); 33 break; 34 default: 35 value = ""; 36 }
如上是對不同數據類型不同的讀取,java的poi讀取中,通常用到的數據類型為:
CELL_TYPE_BLANK 空值
CELL_TYPE_BOOLEAN 布爾型
CELL_TYPE_ERROR 錯誤
CELL_TYPE_FORMULA 公式型
CELL_TYPE_STRING 字符串型
CELL_TYPE_NUMERIC 數值型
d、接下來就是對於不同的數據判定結果,進行的操作不同,在這里我們判定如果正確列直接錄入數據庫,如果不正確的則返回界面
(當然實際應用中應當是全部正確才能錄入,如果存在不正確的數據,整個文件都不能錄入)
1 if (i == 0 && value.trim().equals("")) { 2 break; 3 } 4 values[i] = value; 5 } 6 } 7 } 8 } 9 //進行判定全局變量boolean值all,如果為true,則說明已經數據完全正確,則取值向數據庫中進行存取,如果為false則存在錯誤信息,將得到的錯誤信息直接返回,不再向數據庫發送存取請求 10 } 11 return returnArray; 12 }
最后面這部分寫的略微有些凌亂,這樣我們來回顧一下讀取步驟:
1、獲取文件地址;
2、根據文件地址,讀取文件;
(這里應該是讀取全部數據,可以先進行基本數據的判定,如果存在不對的數據類型,則記錄並返回提示,如果正確了再進行下一步);
3、如果判定之前的數據類型設定都正確了,則進行判定數據的具體規格,例如:編號前幾位必須為http,身份證號碼共18位等;
4、如果以上所有的判定都滿足,則將數據進行錄入數據庫操作,(數據錄入數據庫,相信都不會陌生了吧)
如果存在問題,則應該返回界面進行相應的提示操作。
