再次回到咱们的用户模块上,咱们发现还有两个功能没有完成: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); } }