利用Java自定義格式,循環導出數據、圖片到excel
- 1、自定義格式循環導出數據
- 1.1.設置格式
- 1.1.1、居中樣式
- 1.1.2、應用樣式到合并區域
- 1.1.3、合并單元格
- 1.1.4、設置列寬
- 1.2、寫入數據
- 1.2.1、創建標簽頭部
- 1.2.2、寫入標簽內容
- 2、自定義格式循環導出圖片
- 2.1、設置格式并插入圖片
- 3、格式和數據寫入統一封裝
- 4、測試方法和效果
- 4.1、測試方法
- 4.2、運行效果
1、自定義格式循環導出數據
我有一個需求,需要做一個標簽,格式固定,但是數據從數據庫中獲取,手動一個一個寫不現實,所以我考慮寫個方法,自定義格式,循環的將數據導出到excel。
說明:
因為我要做的一個標簽,占據excel 5行4列,所以在下面的代碼中,會提到 4 5或者這兩數的倍數關系,請注意這個情況
1.1.設置格式
1.1.1、居中樣式
// 創建居中樣式private static CellStyle createCenterStyle(Workbook workbook) {CellStyle style = workbook.createCellStyle();// 水平居中style.setAlignment(HorizontalAlignment.CENTER);// 垂直居中style.setVerticalAlignment(VerticalAlignment.CENTER);// 設置邊框style.setBorderTop(BorderStyle.THIN);style.setBorderBottom(BorderStyle.THIN);style.setBorderLeft(BorderStyle.THIN);style.setBorderRight(BorderStyle.THIN);//字體Font font = workbook.createFont();font.setFontName("微軟雅黑"); // 設置字體類型font.setFontHeightInPoints((short) 8); // 設置字體大小style.setFont(font);return style;}
1.1.2、應用樣式到合并區域
/*** 應用樣式到合并區域* @param sheet 工作表* @param region 合并區域* @param style 樣式*/private static void applyStyleToMergedRegion(Sheet sheet, CellRangeAddress region, CellStyle style) {for (int row = region.getFirstRow(); row <= region.getLastRow(); row++) {Row sheetRow = sheet.getRow(row) != null ? sheet.getRow(row) : sheet.createRow(row);for (int col = region.getFirstColumn(); col <= region.getLastColumn(); col++) {Cell cell = sheetRow.getCell(col) != null ?sheetRow.getCell(col) : sheetRow.createCell(col);cell.setCellStyle(style);}}}
1.1.3、合并單元格
/*** 合并單元格并居中* @param sheet 工作表* @param centerStyle 樣式* @param columnsNum 列數* @param lineNum 行數*/private static void mergeAndCenterCells(Sheet sheet, CellStyle centerStyle,int columnsNum, int lineNum) {for (int i = 0; i < lineNum; i=i+5) {for (int j = 0; j < columnsNum; j=j+4) {//標簽頭部的合并區域 同一行 4列CellRangeAddress titleMerge = new CellRangeAddress(i, i, j, j+3);//圖片合并區域 標簽頭部第2行到第5行 第1列到第2列CellRangeAddress photoMerge = new CellRangeAddress(i+1, i+4, j, j+1);//數據合并區域CellRangeAddress queMerge = new CellRangeAddress(i+3, i+3, j+2, j+3);CellRangeAddress timeMerge = new CellRangeAddress(i+4, i+4, j+2, j+3);//寫入工作表sheet.addMergedRegion(titleMerge);sheet.addMergedRegion(photoMerge);sheet.addMergedRegion(queMerge);sheet.addMergedRegion(timeMerge);// 應用居中樣式到合并區域applyStyleToMergedRegion(sheet, titleMerge, centerStyle);applyStyleToMergedRegion(sheet, photoMerge, centerStyle);applyStyleToMergedRegion(sheet, queMerge, centerStyle);applyStyleToMergedRegion(sheet, timeMerge, centerStyle);}}}
1.1.4、設置列寬
循環設置標簽列寬,以適應圖片和數據,大小 1 * 256 為1個字符。
/*** 設置列的寬度 以適應圖片和數據* @param columnsNum 列數* @param sheet 工作表*/private static void setColumnWidth(int columnsNum,Sheet sheet){//循環設置標簽的列寬for (int i=0;i<columnsNum;i=i+4){sheet.setColumnWidth(i, 4 * 256); // 設置第一列寬度為4個字符sheet.setColumnWidth(i+1, 4 * 256); // 設置第二列寬度為4個字符sheet.setColumnWidth(i+2, 12 * 256); // 設置第三列寬度為12個字符sheet.setColumnWidth(i+3, 12 * 256); // 設置第四列寬度為12個字符}}
1.2、寫入數據
1.2.1、創建標簽頭部
/*** 創建標簽頭部* @param sheet 工作表* @param style 居中樣式* @param mainTitleStr 頭部數據* @param columnsNum 列數* @param lineNum 行數*/private static void createHeader(Sheet sheet, CellStyle style,String mainTitleStr,int columnsNum, int lineNum) {for (int i = 0; i < lineNum; i=i+5) {// 隔5行設置一次Row headerRow = sheet.createRow(i);// 主標題(將被合并)for (int j = 0; j < columnsNum; j=j+4) {//隔4列設置一次Cell mainTitle = headerRow.createCell(j);mainTitle.setCellValue(mainTitleStr);mainTitle.setCellStyle(style);}}}
1.2.2、寫入標簽內容
標簽固定的內容可以在這里先寫好,其他有變動的可以從其他渠道獲取后再去拼接。
/*** 設置內容格式及數據 固定不變的可以在這里寫好 其他從數據庫取到的再去拼接* @param sheet 工作表* @param style 樣式* @param List 從數據庫或其他途徑取到的數據* @param columnsNum 列數* @param lineNum 行數*/private static void createContent(Sheet sheet, CellStyle style,List<Map<Object,Object>> List,int columnsNum, int lineNum) {int lineTemp=0;// 臨時的 行數int columnsTemp=0;// 臨時的 列數for (Map<Object,Object> map:List) {//搞成二位數據 直接固定格式 后續方便固定格式 寫入excel表格Object[][] data = {{"型號:"+map.get("name"), "夾具類型:"+ map.get("type")},{"精度:符合標準", "編號:"+map.get("num")},{"確認人:xxx □ xxx □ "},{"點檢有效期:"+ DateUtil.year(new Date())+" - "+ DateUtil.year(new Date())+" "},};//遍歷二維數組for (int i = 0; i < data.length; i++) {int temp=lineTemp+i + 1;//要生成指定行 行數 +1 是因為數據在標簽頭部下面寫入//這么做的目的是 為了避免重新生成一行,導致之前的數據被刪除Row row=sheet.getRow(temp);//獲得之前生成的行if (row==null){//如果之前未生成行 那將需要去生成row = sheet.createRow(temp);}int cellTemp=columnsTemp+2;//指定哪一列 +2是因為前面兩列是設置圖片for (int j = 0; j < data[i].length; j++) {Cell value = row.createCell(cellTemp);row.createCell(cellTemp).setCellValue(data[i][j].toString());//寫入內容value.setCellStyle(style);//設置格式cellTemp+=1;//下一列}}//為了不超過設置的行數 和列數 做出如下判斷if (columnsTemp<columnsNum && (columnsTemp+4)!=columnsNum){columnsTemp=columnsTemp+4;}else {columnsTemp=0;if (lineTemp<lineNum && (lineTemp+5)!=lineNum){lineTemp=lineTemp+5;}else{lineTemp=0;}}}}
2、自定義格式循環導出圖片
在每個標簽中,我都要插入一個二維碼,位置都是固定的,所以就自定義圖片的位置,并且循環插入不同的二維碼。
2.1、設置格式并插入圖片
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.util.IOUtils;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;/*** 圖片保存到excel的類*/
public class ImageForExcel {/*** 設置圖片保存到excel的位置* @param workbook excel* @param sheet 工作表* @param photoPath 插入圖片的路徑 一個list集合* @param columnsNum 列數* @param lineNum 行數*/public static void setPhoto(Workbook workbook, Sheet sheet, List<String> photoPath, int columnsNum, int lineNum){int index=0;for (int i = 0; i < lineNum; i=i+5) {for (int j = 0; j < columnsNum; j=j+4) {// 讀取圖片文件try (InputStream is = new FileInputStream(photoPath.get(index))) {// 將圖片轉換為字節數組byte[] bytes = IOUtils.toByteArray(is);// 添加圖片到工作簿int pictureIdx = workbook.addPicture(bytes, Workbook.PICTURE_TYPE_JPEG);// 創建繪圖工具CreationHelper helper = workbook.getCreationHelper();Drawing<?> drawing = sheet.createDrawingPatriarch();// 創建錨點,設置圖片位置ClientAnchor anchor = helper.createClientAnchor();anchor.setCol1(j); // 圖片起始列(0-based)anchor.setRow1(i+1); // 圖片起始行(0-based)anchor.setCol2(j+2); // 圖片結束列(0-based)anchor.setRow2(i+5); // 圖片結束行(0-based)// 創建圖片并設置錨點Picture pict = drawing.createPicture(anchor, pictureIdx);index=index+1;// 可選:調整單元格大小以適應圖片
// sheet.setColumnWidth(1, 4 * 256); // 調整列寬
// sheet.setColumnWidth(0, 4 * 256); // 調整列寬/*Row row = sheet.createRow(3);row.setHeightInPoints(100); // 調整行高*/} catch (IOException e) {e.printStackTrace();}}}}
}
3、格式和數據寫入統一封裝
將格式設計和寫入數據的方法同意封裝成一個方法,方便調用。
/*** 統一封裝的方法* @param mainTitleStr 標簽頭部* @param exportPath 導出的路徑 二維碼圖片獲取的路徑* @param columnsNum 標簽的總列數* @param lineNum 標簽的總行數* @param data 變動的數據* @return* @throws Exception*/public static String exportTitle(String mainTitleStr, String exportPath,int columnsNum, int lineNum, List<Map<Object,Object>> data) throws Exception {String excelPath=exportPath+"標簽WZ.xlsx";Workbook workbook = new XSSFWorkbook();Sheet sheet = workbook.createSheet("標簽");//工作表名// 1、設置列寬(單位是1/256個字符寬度)setColumnWidth(columnsNum,sheet);// 設置行高(單位是點,1點=1/20像素)
// Row row = sheet.createRow(0);
// row.setHeightInPoints(30); // 設置行高為30點// 2. 創建居中樣式CellStyle centerStyle = createCenterStyle(workbook);// 3. 設置表頭數據createHeader(sheet, centerStyle,mainTitleStr,columnsNum,lineNum);// 4. 設置內容數據createContent(sheet, centerStyle,data,columnsNum,lineNum);// 5. 設置合并區域并應用居中樣式mergeAndCenterCells(sheet, centerStyle,columnsNum,lineNum);//將圖片插入excelList<String> photoPath=new ArrayList<>();for (Map<Object,Object> map:data) {photoPath.add(exportPath+map.get("mold").toString()+".png");}ImageForExcel.setPhoto(workbook,sheet,photoPath,columnsNum,lineNum);// 7. 保存文件try (FileOutputStream out = new FileOutputStream(excelPath)) {workbook.write(out);}return excelPath;}
4、測試方法和效果
文字數據是我自己編的,如果大家要從數據庫或者其他來源獲取,改動data即可。
二維碼圖片是我利用二維碼生成方法生成的,如果大家有興趣,可以看看我上一篇的博客。點擊這里: 二維碼生成方法
4.1、測試方法
這個是有循環生成多個標簽的功能。
所以列數 行數 需要根據自己的需求。
如不需要循環,只需將行列數設置為一個標簽所需要占據的數即可。
目前我的測試方法是,一個標簽占據5行4列,下面設定是有4個標簽。
如果對代碼中 4 5 或者 4 5 倍數 ,又或者其他數據有疑問的話,可以看看這里
import com.codermy.myspringsecurityplus.utils.qrUtils.TitleForExcel;import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;public class BiaoQian {public static void main(String[] args) throws Exception {//1、表頭String mainTitleStr="夾具確認表 DOC-190-AA0004-01D";//2、excel存放的位置String exportPath="D:/java/codeSpace/testFile/";/*這個是有生成多個的功能 所以列數 行數 需要根據自己的需求如不需要循環,只需將行列數設置為一個標簽所需要占據的數即可目前我的數據是,一個標簽占據5行4列,下面設定是循環寫入4個標簽如果對代碼中 4 5 或者 4 5 倍數 ,又或者其他數據有疑問的話,可以看看這里*///3、列數int columnsNum=8;//4、行數int lineNum=10;//5、數據 可以從數據庫中得到 然后再按照我們需要的數據做處理// 下面是我自己模擬的一些數據List<Map<Object,Object>> data=new ArrayList<>();String name="C40888";String type="WZ";//這個 (i < 5) ,需要對應上面的行列數 有四個標簽for (int i = 1; i < 5; i++) {Map<Object,Object> map=new HashMap<>();map.put("name",name);map.put("type",type);map.put("num",i);map.put("mold",name+"-"+type+"-"+i);data.add(map);}String result=TitleForExcel.exportTitle(mainTitleStr,exportPath,columnsNum,lineNum,data);System.out.println(result);}}
4.2、運行效果
由于圖片有二維碼,會被和諧,所以我在二維碼上搞了貼紙,實際導出效果沒有貼紙。
以上就是本篇文章的全部內容,部分代碼是利用AI生成,然后再去修改成我想要的效果,如果有侵權的地方,還請聯系本人。
本文有很多可以修改優化的地方,如果各位同學有好的見解可以評論留言討論。
如果代碼有異常,或者有其他疑惑、可以評論區留言。