Excel样式
回到我们的需求中,当我们使用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"); }
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); }
Service实现
/** * 第一行写死,字体大小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(); }