再次回到咱們的用戶模塊上,咱們發現還有兩個功能沒有完成:javascript
對於將網頁中的數據導入或導出到excel文件中,咱們是徹底沒有學習過的。可是呢,在Java中操做excel是相對經常使用的,所以也有組件供咱們使用css
JAVA中操做Excel的有兩種比較主流的工具包java
此次咱們主要學習的是POI操做excel。數據庫
JXL有一個缺陷,只能操做03或之前版本的excel,而POI能夠操做97-07版本的。數組
首先,要用Java來操做excel的話,確定用對象來指定excel相關的內容的。咱們來看看excel由什麼組成:瀏覽器
POI是這樣看的:服務器
給咱們一頓分析之後,咱們發現它們之間是有從屬關係的:markdown
導入POI開發包:app
/** * 使用POI1無非操做Excel無非就4個步驟: * * 建立/讀取工做薄 * 建立/讀取工做表 * 建立/讀取行 * 建立/讀取單元格 * * * */
@Test public void testWrite() throws IOException { //建立工做薄 HSSFWorkbook workbook = new HSSFWorkbook(); //建立工做表 HSSFSheet sheet = workbook.createSheet("我是新的工做表"); //建立行,座標從0開始,我建立的是第三行 HSSFRow row = sheet.createRow(2); //建立單元格,座標也是從0開始,因而就是第三行第三列 HSSFCell cell = row.createCell(2); //往單元格寫數據 cell.setCellValue("helloWorld"); //把工做薄寫到硬盤中 FileOutputStream outputStream = new FileOutputStream("C:\\工做薄.xls"); workbook.write(outputStream); //關閉流 workbook.close(); outputStream.close(); }
@Test public void testRead() throws IOException { //獲取輸入流,讀取Excel數據 FileInputStream inputStream = new FileInputStream("C:\\工做薄.xls"); //建立工做薄 HSSFWorkbook workbook = new HSSFWorkbook(inputStream); //獲得工做表 HSSFSheet sheet = workbook.getSheetAt(0); //獲得行 HSSFRow row = sheet.getRow(2); //獲得單元格 HSSFCell cell = row.getCell(2); //獲得單元格的數據 String cellValue = cell.getStringCellValue(); System.out.println(cellValue); }
其實他們的方法都是同樣的,僅僅是類的不一樣。而使用哪一個對象,咱們能夠根據後綴名來判斷建立哪一個對象【是03仍是07】dom
@Test public void testRead03And07Excel() throws Exception { String fileName = "D:\\itcast\\測試.xlsx"; if(fileName.matches("^.+\\.(?i)((xls)|(xlsx))$")){//判斷是否excel文檔 boolean is03Excel = fileName.matches("^.+\\.(?i)(xls)$"); FileInputStream inputStream = new FileInputStream(fileName); //一、讀取工做簿 Workbook workbook = is03Excel ?new HSSFWorkbook(inputStream):new XSSFWorkbook(inputStream); //二、讀取第一個工做表 Sheet sheet = workbook.getSheetAt(0); //三、讀取行;讀取第3行 Row row = sheet.getRow(2); //四、讀取單元格;讀取第3行第3列 Cell cell = row.getCell(2); System.out.println("第3行第3列單元格的內容爲:" + cell.getStringCellValue()); workbook.close(); inputStream.close(); } }
回到咱們的需求中,當咱們使用POI導出數據的時候,Excel應該要有樣式纔好看的。相似下面的模板:
在POI中能夠利用格式化對象來格式化excel文檔;也即設置excel內容的樣式。
POI中主要的格式化對象經常使用的有:
POI的樣式對象明顯是屬性工做薄的。應用於工做表
屬於工做薄,應用於工做表
建立合併單元格對象的時候要給出4個參數,它們分別表示:
@Test public void testCellRange() throws IOException { //建立工做薄 HSSFWorkbook workbook = new HSSFWorkbook(); //建立合併單元格對象,從第六行開始到第十行,從第六列開始,到第十列 CellRangeAddress cellRangeAddress = new CellRangeAddress(5, 9, 5, 9); //建立工做表 HSSFSheet sheet = workbook.createSheet("我是新的工做表"); //應用於工做表 sheet.addMergedRegion(cellRangeAddress); //建立行,座標從0開始,我建立的是第六行 HSSFRow row = sheet.createRow(5); //建立單元格,座標也是從0開始,因而就是第六行第六列 HSSFCell cell = row.createCell(5); //往單元格寫數據 cell.setCellValue("helloWorld"); //把工做薄寫到硬盤中 FileOutputStream outputStream = new FileOutputStream("C:\\工做薄.xls"); workbook.write(outputStream); //關閉流 workbook.close(); outputStream.close(); }
上面的圖咱們能夠發現,咱們已經實現了合併單元格,可是通常咱們都是將字體設置成居中、字體大小等等。POI也提供了相對應的對象給咱們實現:
樣式屬於工做薄,應用於單元格:
@Test public void test() throws IOException { //建立工做薄 HSSFWorkbook workbook = new HSSFWorkbook(); //建立樣式對象 HSSFCellStyle style = workbook.createCellStyle(); //建立合併單元格對象,從第六行開始到第十行,從第六列開始,到第十列 CellRangeAddress cellRangeAddress = new CellRangeAddress(5, 9, 5, 9); //設置水平居中 style.setAlignment(HSSFCellStyle.ALIGN_CENTER); //設置垂直居中 style.setVerticalAlignment(HSSFCellStyle.VERTICAL_CENTER); //建立工做表 HSSFSheet sheet = workbook.createSheet("我是新的工做表"); sheet.addMergedRegion(cellRangeAddress); //建立行,座標從0開始,我建立的是第六行 HSSFRow row = sheet.createRow(5); //建立單元格,座標也是從0開始,因而就是第六行第六列 HSSFCell cell = row.createCell(5); //往單元格寫數據 cell.setCellValue("helloWorld"); //設置單元格的樣式 cell.setCellStyle(style); //把工做薄寫到硬盤中 FileOutputStream outputStream = new FileOutputStream("C:\\工做薄.xls"); workbook.write(outputStream); //關閉流 workbook.close(); outputStream.close(); }
字體屬於工做薄,應用於樣式【和css是相似的】
@Test public void test() throws IOException { //建立工做薄 HSSFWorkbook workbook = new HSSFWorkbook(); //建立樣式對象 HSSFCellStyle style = workbook.createCellStyle(); //建立合併單元格對象,從第六行開始到第十行,從第六列開始,到第十列 CellRangeAddress cellRangeAddress = new CellRangeAddress(5, 9, 5, 9); //設置水平居中 style.setAlignment(HSSFCellStyle.ALIGN_CENTER); //設置垂直居中 style.setVerticalAlignment(HSSFCellStyle.VERTICAL_CENTER); //建立font對象 HSSFFont font = workbook.createFont(); //設置字體爲粗體 font.setBoldweight(HSSFFont.BOLDWEIGHT_BOLD); //字體爲23字號 font.setFontHeightInPoints((short) 23); //設置字體的顏色 font.setColor(HSSFFont.COLOR_RED); //字體應用於樣式 style.setFont(font); //建立工做表 HSSFSheet sheet = workbook.createSheet("我是新的工做表"); sheet.addMergedRegion(cellRangeAddress); //建立行,座標從0開始,我建立的是第六行 HSSFRow row = sheet.createRow(5); //建立單元格,座標也是從0開始,因而就是第六行第六列 HSSFCell cell = row.createCell(5); //往單元格寫數據 cell.setCellValue("helloWorld"); //設置單元格的樣式 cell.setCellStyle(style); //把工做薄寫到硬盤中 FileOutputStream outputStream = new FileOutputStream("C:\\工做薄.xls"); workbook.write(outputStream); //關閉流 workbook.close(); outputStream.close(); }
綁定按鈕事件,請求Action處理導出,打開一個輸入框給用戶下載
function doExportExcel() { window.open("${basePath}user/user_exportExcel.action"); }
/************導出Excel*************************/ public void exportExcel() throws IOException { //查找出列表的所有數據 List<User> list = userServiceImpl.findObjects(); //導出其實就是讓用戶下載該Excel文件 HttpServletResponse response = ServletActionContext.getResponse(); //設置頭和指定名稱 response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode("列表展現.xls", "UTF-8")); //指定返回的類容數據 response.setContentType("application/x-execl"); ServletOutputStream outputStream = response.getOutputStream(); //給Service層作導出Excel操做 userServiceImpl.exportExcel(list, outputStream); }
/** * 第一行寫死,字體大小11,居中,粗體,合併單元格 * 第二行寫死,粗體 * 第三行開始,是數據庫列表的數據 */ @Override public void exportExcel(List<User> list, ServletOutputStream outputStream) { /***********建立工做薄---樣式---字體--單元格*************/ HSSFWorkbook workbook = new HSSFWorkbook(); //第一行的合併單元格 CellRangeAddress cellRangeAddress = new CellRangeAddress(0, 0, 0, 4); //建立第一行樣式【居中】 HSSFCellStyle cellStyle = workbook.createCellStyle(); cellStyle.setVerticalAlignment(HSSFCellStyle.VERTICAL_CENTER); cellStyle.setAlignment(HSSFCellStyle.ALIGN_CENTER); //建立第二行樣式【居中】 HSSFCellStyle cellStyle2 = workbook.createCellStyle(); cellStyle2.setVerticalAlignment(HSSFCellStyle.VERTICAL_CENTER); cellStyle2.setAlignment(HSSFCellStyle.ALIGN_CENTER); //建立第一行字體 HSSFFont font = workbook.createFont(); font.setFontHeightInPoints((short) 23); font.setBoldweight(HSSFFont.BOLDWEIGHT_BOLD); //建立第二行字體 HSSFFont font2 = workbook.createFont(); font2.setBoldweight(HSSFFont.BOLDWEIGHT_BOLD); //字體應用於樣式 cellStyle.setFont(font); cellStyle2.setFont(font2); /***********建立工做表*************/ HSSFSheet sheet = workbook.createSheet("用戶列表"); //第一行單元格應用於工做表 sheet.addMergedRegion(cellRangeAddress); //設置默認列寬 sheet.setDefaultColumnWidth(25); /***********建立行*************/ //第一行 HSSFRow row = sheet.createRow(0); HSSFCell cell = row.createCell(0); cell.setCellStyle(cellStyle); cell.setCellValue("用戶列表"); //第二行數據也是寫死的,咱們用數組遍歷便可 String[] data = {"用戶名","賬號", "所屬部門", "性別", "電子郵箱"}; HSSFRow row1 = sheet.createRow(1); for (int i = 0; i < data.length; i++) { HSSFCell cell1 = row1.createCell(i); cell1.setCellValue(data[i]); //加載第二行樣式 cell1.setCellStyle(cellStyle2); } /***************行和列在循環的時候,不要重複了。否則會報錯的!!!!*****************/ //第三行數據就是咱們數據庫保存的數據 if (list != null) { int i=2; for (User user : list) { //從第三行開始 HSSFRow row2 = sheet.createRow(i); HSSFCell row2Cel0 = row2.createCell(0); row2Cel0.setCellValue(user.getName()); HSSFCell row2Cell = row2.createCell(1); row2Cell.setCellValue(user.getAccount()); HSSFCell row2Cel2 = row2.createCell(2); row2Cel2.setCellValue(user.getDept()); HSSFCell row2Cel3 = row2.createCell(3); row2Cel3.setCellValue(user.isGender() ? "男" : "女"); HSSFCell row2Cel4 = row2.createCell(4); row2Cel4.setCellValue(user.getEmail()); i++; } } try { //寫到outputSteam上 workbook.write(outputStream); workbook.close(); outputStream.close(); } catch (IOException e) { e.printStackTrace(); } }
咱們來看下面這段代碼,他們都要設置居中,字體就除了大小不一樣。其餘都相同。卻佔用了這麼多代碼!!!
//建立第一行樣式【居中】 HSSFCellStyle cellStyle = workbook.createCellStyle(); cellStyle.setVerticalAlignment(HSSFCellStyle.VERTICAL_CENTER); cellStyle.setAlignment(HSSFCellStyle.ALIGN_CENTER); //建立第二行樣式【居中】 HSSFCellStyle cellStyle2 = workbook.createCellStyle(); cellStyle2.setVerticalAlignment(HSSFCellStyle.VERTICAL_CENTER); cellStyle2.setAlignment(HSSFCellStyle.ALIGN_CENTER); //建立第一行字體 HSSFFont font = workbook.createFont(); font.setFontHeightInPoints((short) 23); font.setBoldweight(HSSFFont.BOLDWEIGHT_BOLD); //建立第二行字體 HSSFFont font2 = workbook.createFont(); font2.setBoldweight(HSSFFont.BOLDWEIGHT_BOLD); //字體應用於樣式 cellStyle.setFont(font); cellStyle2.setFont(font2);
因而我就抽取成一個方法來獲得樣式
/** * @param workbook 當前使用工做薄 * @param fontSize 字體大小 * * */ public HSSFCellStyle createStyle(HSSFWorkbook workbook, short fontSize) { HSSFCellStyle cellStyle = workbook.createCellStyle(); cellStyle.setVerticalAlignment(HSSFCellStyle.VERTICAL_CENTER); cellStyle.setAlignment(HSSFCellStyle.ALIGN_CENTER); HSSFFont font = workbook.createFont(); font.setFontHeightInPoints(fontSize); font.setBoldweight(HSSFFont.BOLDWEIGHT_BOLD); cellStyle.setFont(font); return cellStyle; }
當使用的時候,代碼就變成了這樣調用:
HSSFCellStyle cellStyle = createStyle(workbook, (short) 24); HSSFCellStyle cellStyle2 = createStyle(workbook, (short) 13);
咱們的Service業務層的代碼看起來太多了。這樣咱們維護起來就不方便了。
個人作法是:把代碼抽取成Utils的方法,Service層調用就行了。
如今我有這麼一個Excel文件,要把信息存儲到數據庫中,而且在瀏覽器顯示出來
其實導入Excel就是文件上傳,只不過不用把文件保存在服務器的硬盤數據中而是保存在數據庫中,輸出到瀏覽器就好了。
function doImportExcel() { document.forms[0].action = "${basePath}user/user_importExcel.action"; document.forms[0].submit(); }
/*************上傳Excel************************/ private File userExcel; private String userExcelFileName; private String userExcelContentType; public void setUserExcel(File userExcel) { this.userExcel = userExcel; } public void setUserExcelFileName(String userExcelFileName) { this.userExcelFileName = userExcelFileName; } public void setUserExcelContentType(String userExcelContentType) { this.userExcelContentType = userExcelContentType; }
主要判斷有沒有上傳文件。給Service層處理
/************導入Excel*************************/ public String importExcel() throws IOException { //一、獲取excel文件 if(userExcel != null){ //是不是excel if(userExcelFileName.matches("^.+\\.(?i)((xls)|(xlsx))$")){ //二、導入 userServiceImpl.importExcel(userExcel, userExcelFileName); } } return "list"; }
public static List<User> importExcel(File userExcel, String userExcelFileName) { try { FileInputStream fileInputStream = new FileInputStream(userExcel); boolean is03Excel = userExcelFileName.matches("^.+\\.(?i)(xls)$"); //一、讀取工做簿 Workbook workbook = is03Excel ? new HSSFWorkbook(fileInputStream) : new XSSFWorkbook(fileInputStream); //二、讀取工做表 Sheet sheet = workbook.getSheetAt(0); //三、讀取行 List<User> users = new ArrayList<>(); if (sheet.getPhysicalNumberOfRows() > 2) { User user = null; for (int k = 2; k < sheet.getPhysicalNumberOfRows(); k++) { //四、讀取單元格 Row row = sheet.getRow(k); user = new User(); //用戶名 Cell cell0 = row.getCell(0); user.setName(cell0.getStringCellValue()); //賬號 Cell cell1 = row.getCell(1); user.setAccount(cell1.getStringCellValue()); //所屬部門 Cell cell2 = row.getCell(2); user.setDept(cell2.getStringCellValue()); //性別 Cell cell3 = row.getCell(3); user.setGender(cell3.getStringCellValue().equals("男")); //手機號 String mobile = ""; Cell cell4 = row.getCell(4); try { mobile = cell4.getStringCellValue(); } catch (Exception e) { double dMobile = cell4.getNumericCellValue(); mobile = BigDecimal.valueOf(dMobile).toString(); } user.setMobile(mobile); //電子郵箱 Cell cell5 = row.getCell(5); user.setEmail(cell5.getStringCellValue()); //生日 Cell cell6 = row.getCell(6); if (cell6.getDateCellValue() != null) { user.setBirthday(cell6.getDateCellValue()); } //默認用戶密碼爲 123456 user.setPassword("123456"); //默認用戶狀態爲 有效 user.setState(User.USER_STATE_VALID); users.add(user); } } workbook.close(); fileInputStream.close(); return users; } catch (Exception e) { e.printStackTrace(); } return null; }
public void importExcel(File userExcel, String userExcelFileName) { List<User> users = ExcelUtils.importExcel(userExcel, userExcelFileName); for (User user : users) { save(user); } }