您好,登錄后才能下訂單哦!
使用POI怎么生成一個EXCEL文件?針對這個問題,這篇文章詳細介紹了相對應的分析和解答,希望可以幫助更多想解決這個問題的小伙伴找到更簡單易行的方法。
一、背景
Apache POI 是創建和維護操作各種符合Office Open XML(OOXML)標準和微軟的OLE 2復合文檔格式(OLE2)的Java API。用它可以使用Java讀取和創建,修改MS Excel文件.而且,還可以使用Java讀取和創建MS Word和MSPowerPoint文件。Apache POI 提供Java操作Excel解決方案(適用于Excel97-2008)。
根據指定格式的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
添加圖片信息
輸出文件流
功能入口如下
@Override public void buildOutputStream() throws FileProducerException { // 處理傳入的JSON數據 sheets = this.jsonData.getJSONArray(this.SHEETS); Iterator<Object> sheetIter = sheets.iterator(); if (sheets.isEmpty()) { this.responseData.setErrcode(1001); this.responseData.setSuccess(false); this.responseData.setErrmsg("無數據可生成"); throw new FileProducerException(); } wb = new XSSFWorkbook(); // 建立全局格式 JSONArray userStyles = this.jsonData.getJSONArray(this.USERSTYLES); this.initUserStyles(userStyles); this.initDefaultHeadStyle(); XSSFSheet ws; JSONObject sheet; JSONArray sheetData; JSONArray sheetTitle; JSONArray sheetHead; JSONArray sheetFoot; JSONArray sheetImgs; String sheetName; int sheetIndex = 0; while (sheetIter.hasNext()) { sheet = (JSONObject) sheetIter.next(); // 獲取sheet名稱 sheetName = sheet.getString(this.SHEET_NAME); ws = wb.createSheet(); if (StringUtils.isNotBlank(sheetName)) { wb.setSheetName(sheetIndex, sheetName); } int sheetRowIndex = 0; sheetTitle = sheet.getJSONArray(this.SHEET_TITLE); this.setMergeCells(ws, sheet.getJSONArray(this.SHEET_TITLE_MERGE), sheetRowIndex); sheetRowIndex = this.createRandom(ws, sheetTitle, sheetRowIndex); sheetHead = sheet.getJSONArray(this.SHEET_HEAD); sheetRowIndex = this.createHeadColumn(ws, sheetHead, sheetRowIndex); this.setMergeCells(ws, sheet.getJSONArray(this.SHEET_DATA_MERGE), sheetRowIndex); sheetData = sheet.getJSONArray(this.SHEET_DATA); sheetRowIndex = this.createData(ws, sheetData, sheetRowIndex); sheetFoot = sheet.getJSONArray(this.SHEET_FOOT); this.setMergeCells(ws, sheet.getJSONArray(this.SHEET_FOOT_MERGE), sheetRowIndex); sheetRowIndex = this.createRandom(ws, sheetFoot, sheetRowIndex); sheetImgs = sheet.getJSONArray(this.SHEET_IMG); this.setSheetImages(ws, sheetImgs); } // 返回輸出流 try { ByteArrayOutputStream os = new ByteArrayOutputStream(); wb.write(os); this.outStreams.add(os); } catch (IOException e) { throw new FileProducerException(e.getMessage(), e.getCause()); } }
生成單元格樣式對象,包括字體 邊框 背景 對齊方式
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())); // 自動換行TRUE cellStyle.setWrapText(true); // 格式 String fmt = style.getString(this.FMTSTRING); if (StringUtils.isNotBlank(fmt)) cellStyle.setDataFormat(wb.createDataFormat().getFormat(fmt)); return cellStyle; }
創建字體樣式
private Font createFont(JSONObject fontCfg) { if (fontCfg == null) return null; XSSFFont font = wb.createFont(); font.setFontName(fontCfg.getString(this.FONT_NAME)); Boolean fontBoole = fontCfg.getBoolean(FONT_BLOD); if (fontBoole != null) font.setBold(fontBoole.booleanValue()); fontBoole = fontCfg.getBoolean(this.FONT_ITALIC); if (fontBoole != null) font.setItalic(fontBoole.booleanValue()); fontBoole = fontCfg.getBoolean(this.FONT_UNDERLINE); if (fontBoole != null && fontBoole.booleanValue() == true) font.setUnderline(FontUnderline.SINGLE.getByteValue()); Short fontHeight = fontCfg.getShort(this.FONT_HEIGHT); if (fontHeight != null) font.setFontHeightInPoints(fontHeight); String colorStr = fontCfg.getString(this.FONT_COLOR); if (colorStr != null) { font.setColor(new XSSFColor(new Color(Integer.parseInt( colorStr.substring(1), 16)))); } return font; }
處理表頭,表過多表頭處理,采用 | 分割的方式,傳入head長度為列數據,name中有幾個 | 就知道表頭有幾行。所以針對表頭處理有以下幾個步驟
生成默認列樣式
填充所有列數據,求出最大行數
橫向合并內容相同的單元
縱向合并空白的單元格
private int createHeadColumn(XSSFSheet ws, JSONArray sheetHead, int sheetRowIndex) { if (sheetHead == null) return sheetRowIndex; Iterator<Object> headIter = sheetHead.iterator(); JSONObject curHead = null; int colIndex = 0; Object colStyle = null; int colSize = sheetHead.size(); headTypes = new String[colSize]; headCellStyleKeys = new String[colSize]; int[] headColLevel = new int[colSize]; String colName = null; String[] colNameAry = null; int maxLevel = 0; int colLevel = 0; XSSFCell headCell = null; ArrayList<ArrayList<String>> headValueList = new ArrayList<ArrayList<String>>(); while (headIter.hasNext()) { curHead = (JSONObject) headIter.next(); // 處理默認樣式 if (curHead.containsKey(this.COLUMN_STYLE)) { colStyle = curHead.get(this.COLUMN_STYLE); if (colStyle instanceof JSONObject) { headCellStyleKeys[colIndex] = this.COLUMNSTYLE_PREV + colIndex; this.userStyles.put(headCellStyleKeys[colIndex], this.createCellStyle((JSONObject) colStyle)); } else if (this.userStyles.containsKey(colStyle)) { headCellStyleKeys[colIndex] = (String) colStyle; } } // 處理默認列寬 if (curHead.containsKey(this.COLUMN_WIDTH)) { ws.setDefaultColumnWidth(pixToExcelWdith(curHead .getIntValue(this.COLUMN_WIDTH))); } // 保存列樣式 if (curHead.containsKey(this.COLUMN_TYPE)) { headTypes[colIndex] = curHead.getString(this.COLUMN_TYPE); } else { headTypes[colIndex] = this.CELLTYPESTRING; } // 處理多表頭 colName = curHead.getString(this.COLUMN_NAME); colNameAry = colName.split("\\|"); colLevel = colNameAry.length; headColLevel[colIndex] = colLevel; if (colLevel > maxLevel) { maxLevel = colLevel; } for (int i = 0; i < colLevel; i++) { if (headValueList.size() <= i) { headValueList.add(new ArrayList<String>()); } headValueList.get(i).add(colIndex, colNameAry[i]); XSSFRow row = ws.getRow(sheetRowIndex + i); if (row == null) { row = ws.createRow(sheetRowIndex + i); } headCell = row.createCell(colIndex); headCell.setCellValue(colNameAry[i]); headCell.setCellStyle(this.userStyles.get(this.HEADSTYLE_KEY)); } colIndex++; } // 橫向合并 Iterator<ArrayList<String>> a = headValueList.iterator(); JSONArray headMerge = new JSONArray(); String prev = ""; String curent = null; int lRowIndex = 0; int startCol = 0; int mergeCol = 0; ArrayList<String> columnInfo = null; while (a.hasNext()) { startCol = 0; mergeCol = 0; prev = ""; columnInfo = a.next(); // 第三列才能知道,第一列和第二列是否合并 columnInfo.add(""); Iterator<String> b = columnInfo.iterator(); XSSFCell lastRowCell = null; while (b.hasNext()) { curent = b.next(); if (lRowIndex > 0) { lastRowCell = ws.getRow(sheetRowIndex + lRowIndex - 1) .getCell(startCol); } if (prev.equalsIgnoreCase(curent) && lRowIndex == 0) { ws.getRow(sheetRowIndex + lRowIndex).getCell(startCol) .setCellType(Cell.CELL_TYPE_BLANK); mergeCol++; } else if (prev.equalsIgnoreCase(curent) && lRowIndex > 0 && StringUtils .isBlank(lastRowCell.getStringCellValue())) { ws.getRow(sheetRowIndex + lRowIndex).getCell(startCol) .setCellType(Cell.CELL_TYPE_BLANK); mergeCol++; } else { if (mergeCol > 0 && startCol > 0) { headMerge.add(String.format("%d,%d,%d,%d", lRowIndex, lRowIndex, startCol - mergeCol - 1, startCol - 1)); mergeCol = 0; } } startCol++; prev = curent; } lRowIndex++; } for (int i = 0; i < colSize; i++) { if (headColLevel[i] < maxLevel) { // 存在列合并 headMerge.add(String.format("%d,%d,%d,%d", headColLevel[i] - 1, maxLevel - 1, i, i)); for (int r = headColLevel[i]; r < maxLevel; r++) { ws.getRow(sheetRowIndex + r) .createCell(i) .setCellStyle( this.userStyles.get(this.HEADSTYLE_KEY)); } } } this.setMergeCells(ws, headMerge, sheetRowIndex); return sheetRowIndex + maxLevel; }
添加圖片,默認采用單元格描點方式,將圖片固定指定的單元格區域內
private void addImg(XSSFSheet ws, JSONObject img, XSSFCreationHelper cHelper) { String imgBase64 = img.getString(this.SHEET_IMG_DATA); if (StringUtils.isBlank(imgBase64)) return; String[] imgary = imgBase64.split(","); System.out.println(imgary[0]); byte[] imgByte = Base64.decodeBase64(imgary[1]); int imgIdx = wb.addPicture(imgByte, Workbook.PICTURE_TYPE_JPEG); XSSFDrawing drawImg = ws.createDrawingPatriarch(); XSSFClientAnchor anchor = cHelper.createClientAnchor(); int col = img.getIntValue(this.SHEET_IMG_COL); int row = img.getIntValue(this.SHEET_IMG_ROW); anchor.setCol1(col); anchor.setRow1(row); XSSFPicture pict = drawImg.createPicture(anchor, imgIdx); Integer colSpan = img.getInteger(this.SHEET_IMG_COLSPAN); if (colSpan == null) colSpan = 1; Integer rowSpan = img.getInteger(this.SHEET_IMG_ROWSPAN); if (rowSpan == null) rowSpan = 1; pict.resize(colSpan, rowSpan); }
重點說明
對于各數據區域數據,保持區域數據獨立性(數據索引值)
對于圖片開始行和開始列,索引值是針對一個完整的sheet
對于表頭區域,多表頭采用 | 分割,減少部分傳輸數據
excel中style為所有sheet共享樣式。
關于使用POI怎么生成一個EXCEL文件問題的解答就分享到這里了,希望以上內容可以對大家有一定的幫助,如果你還有很多疑惑沒有解開,可以關注億速云行業資訊頻道了解更多相關知識。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。