POI生成EXCEL文件
来源:网络整理 网络用户发布,如有版权联系网管删除 2018-07-04
一、背景
根据指定格式的JSON
文件生成对应的excel
文件,需求如下
- 支持多sheet
- 支持单元格合并
- 支持插入图片
- 支持单元格样式可定制
- 需要 标题(title),表头(head),数据(data) ,表尾(foot) 明确区分
二、效果预览
三、数据格式
由于是生成Excel文件,这里值考虑生成xlsx格式的Excel文件,数据多表头默认考虑使用 | 表示,不在使用colspan rowspan作为。如需要表示两列两行,第一列合并表头格式为: A|B,A|C生成的表格为
A B C 前端通过post的方式将需要生成的数据构造成符合要求的JSON文件提交跟后台。根据以上需求定义JSON格式如下
{"saveName": "生成Excel的文件名.xlsx","userStyles": [{"id": "1", //不能出现重复,在需要设置单元样式的地方,可以直接将style赋值为此值"style": {"font": { //设置字体基本格式"blod": true,//是否加粗"italic": true, //是否倾斜"color": "#FF0000",//字体颜色"name": "微软雅黑", //字体名称"height": 20 //大小},"fmtStr": "", //单元格格式,#,##0.00_);#,##0.00;0 千分位"align": "",//水平对齐方式 left right center"valign": "",//垂直对齐方式 top center bottom"borderColor": "", //设置边框颜色 如 #FF0000"bgColor": "" //设置单元格填充颜色}}],"sheets": [{"sheetName": "", //sheet名称"title": [], // 对应Sheet标题区域数据"titleMerge": [], //对应Sheet标题区域合并信息"head": [{}], //表头信息"data": [], //数据信息"dataMerge": [], //数据合并信息"foot": [], //表尾信息"footMerge": [], //表尾合并信息"img": [] //图片信息,需要将图片转换base64}]}
简要说明
- head 数组中为JSON对象格式为
{"name": "A|B", //表头名称,多表头用|分割"type": "str", //此列数据类型 str num ,在excel中日期也是数字类型,通过fmtStr,显示为日期格式"field": "F_FIELD1", //备用字段,可不用"style": { //此列数据为列默认样式,可以是Style对象,也可以是在userStyles中定义的id值"align": "center"}}
- 在数组 title data foot 中,列表中的数据,可以是一个单独的值如 1,”a”,也可以是一个对象,当为对象时,格式为
{"value": "", //单元格具体的值"type": "", //单元格类型,默认str"style": {} //单元格样式 可以是Style对象,也可以是在userStyles中定义的id值,如果没设置,默认取head总此列对应的style}
- titleMerge、dataMerge、footMerge数组值为逗号分隔的字符串,其含义为
"开始行,结束行,开始列,结束列"
,索引从0开始。如在title中有两行三列数据,现在需要合并一行两列数据对应的值为"0,0,0,1"
- img数组中值为对象,格式
{"col": 1, //图片开始列"row": 0, //开始行"colSpan": 1,//列跨度,最小值1"rowSpan": 2, //行跨度,最小值1"data": "" //base64图片数据如: "data:image/png;base64,iVBO...ggg=="}
四、关键实现
07以后的Excle文件,其实是一个压缩包,里边是一个个的xml文件,其中每一个sheet是一个xml文件,样式是一个xml文件,图片是对应的图片文件,放在media文件夹中,所以,代码思路依次为
- 构建 XSSFWorkbook 对象
- 生成样式
- 依次生成,title head data foot 行数据
- 依次处理合并信息 titlemerge datamerge footmerge
- 添加图片信息
- 输出文件流
功能入口如下


1 @Override 2 public void buildOutputStream() throws FileProducerException { 3 // 处理传入的JSON数据 4 sheets = this.jsonData.getJSONArray(this.SHEETS); 5 Iterator
生成单元格样式对象,包括字体
边框
背景
对齐方式


private XSSFCellStyle createCellStyle(JSONObject style) { XSSFCellStyle cellStyle = wb.createCellStyle();// 设置字体JSONObject font = style.getJSONObject(this.STYLE_FONT);Font excelFont = this.createFont(font);if (excelFont != null) {cellStyle.setFont(excelFont);}// border统一黑色cellStyle.setBorderBottom(BorderStyle.THIN);cellStyle.setBorderTop(BorderStyle.THIN);cellStyle.setBorderLeft(BorderStyle.THIN);cellStyle.setBorderRight(BorderStyle.THIN); String borderColor = style.getString(this.BORDER_COLOR);if (StringUtils.isNotBlank(borderColor)) {XSSFColor xfBorderColor = new XSSFColor(new Color(Integer.parseInt(borderColor.substring(1), 16)));cellStyle.setBorderColor(BorderSide.BOTTOM, xfBorderColor);cellStyle.setBorderColor(BorderSide.TOP, xfBorderColor);cellStyle.setBorderColor(BorderSide.LEFT, xfBorderColor);cellStyle.setBorderColor(BorderSide.RIGHT, xfBorderColor);}// 背景色String bgColor = style.getString(this.BACKGROUND_COLOR);if (StringUtils.isNotBlank(bgColor)) {XSSFColor cellBgColor = new XSSFColor(new Color(Integer.parseInt(bgColor.substring(1), 16)));cellStyle.setFillForegroundColor(cellBgColor);cellStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);}// 对齐方式String hAlignment = style.getString(this.HALIGNMENT);if (StringUtils.isNotBlank(hAlignment))cellStyle.setAlignment(HorizontalAlignment.valueOf(hAlignment.toUpperCase()));String vAlignment = style.getString(this.VALIGNMENT);if (StringUtils.isNotBlank(vAlignment))cellStyle.setVerticalAlignment(VerticalAlignment.valueOf(vAlignment.toUpperCase()));// 自动换行TRUEcellStyle.setWrapText(true); // 格式String fmt = style.getString(this.FMTSTRING);if (StringUtils.isNotBlank(fmt))cellStyle.setDataFormat(wb.createDataFormat().getFormat(fmt));return cellStyle;}
创建字体样式


1 private Font createFont(JSONObject fontCfg) { 2 if (fontCfg == null) 3 return null; 4 XSSFFont font = wb.createFont(); 5 font.setFontName(fontCfg.getString(this.FONT_NAME)); 6 Boolean fontBoole = fontCfg.getBoolean(FONT_BLOD); 7 if (fontBoole != null) 8 font.setBold(fontBoole.booleanValue()); 9 fontBoole = fontCfg.getBoolean(this.FONT_ITALIC);10 if (fontBoole != null)11 font.setItalic(fontBoole.booleanValue());12 fontBoole = fontCfg.getBoolean(this.FONT_UNDERLINE);13 if (fontBoole != null && fontBoole.booleanValue() == true)14 font.setUnderline(FontUnderline.SINGLE.getByteValue());15 Short fontHeight = fontCfg.getShort(this.FONT_HEIGHT);16 if (fontHeight != null)17 font.setFontHeightInPoints(fontHeight);18 String colorStr = fontCfg.getString(this.FONT_COLOR);19 if (colorStr != null) {20 font.setColor(new XSSFColor(new Color(Integer.parseInt(21 colorStr.substring(1), 16))));22 }23 return font;24 }
处理表头,表过多表头处理,采用 | 分割的方式,传入head长度为列数据,name中有几个 | 就知道表头有几行。所以针对表头处理有以下几个步骤
- 生成默认列样式
- 填充所有列数据,求出最大行数
- 横向合并内容相同的单元
- 纵向合并空白的单元格


1 private int createHeadColumn(XSSFSheet ws, JSONArray sheetHead, 2 int sheetRowIndex) { 3 if (sheetHead == null) 4 return sheetRowIndex; 5 Iterator
添加图片,默认采用单元格描点方式,将图片固定指定的单元格区域内


1 private void addImg(XSSFSheet ws, JSONObject img, XSSFCreationHelper cHelper) { 2 String imgBase64 = img.getString(this.SHEET_IMG_DATA); 3 if (StringUtils.isBlank(imgBase64)) 4 return; 5 String[] imgary = imgBase64.split(","); 6 System.out.println(imgary[0]); 7 byte[] imgByte = Base64.decodeBase64(imgary[1]); 8 int imgIdx = wb.addPicture(imgByte, Workbook.PICTURE_TYPE_JPEG); 9 XSSFDrawing drawImg = ws.createDrawingPatriarch();10 XSSFClientAnchor anchor = cHelper.createClientAnchor();11 int col = img.getIntValue(this.SHEET_IMG_COL);12 int row = img.getIntValue(this.SHEET_IMG_ROW);13 anchor.setCol1(col);14 anchor.setRow1(row);15 XSSFPicture pict = drawImg.createPicture(anchor, imgIdx);16 Integer colSpan = img.getInteger(this.SHEET_IMG_COLSPAN);17 if (colSpan == null)18 colSpan = 1;19 Integer rowSpan = img.getInteger(this.SHEET_IMG_ROWSPAN);20 if (rowSpan == null)21 rowSpan = 1;22 pict.resize(colSpan, rowSpan);23 }
五、总结
这次通过传入JSON对象生成样式丰富的excel文件,对于POI操作office文档又更加熟悉一些。相对于解析excel文档,生成就不用考虑文件格式,如:兼容2003格式,考虑大文件sax方式解析。相对于js前端生成excel文件,增加了对生成后文件二次加工的可能性,所以在功能入口中,采用了生成二进制流的方式。文件生成好后,可以继续发送邮件,上传ftp等操作。
重点说明
- 对于各数据区域数据,保持区域数据独立性(数据索引值)
- 对于图片开始行和开始列,索引值是针对一个完整的sheet
- 对于表头区域,多表头采用 | 分割,减少部分传输数据
- excel中style为所有sheet共享样式。
查看评论 回复