1. 程式人生 > 其它 >easyExcel+POI 資料橫向平鋪匯出

easyExcel+POI 資料橫向平鋪匯出

技術標籤:easyexcelpoijava

業務開發中經常會遇到匯出excel的情況,我們大都會藉助第三方工具如 Apache POI alibaba/easyexcel

easy/excel 已經提供了模板填充excel的方法,但是如果遇到如下所示的複雜的橫向平鋪資料,簡單的模板填充則不能達到效果

橫向平鋪資料
示例資料

表格中的輪數資料也是不確定的,其資料結構如下所示

[
{
"brand": "brand",
"buzzRound": 2,
"cost": 2.91,
"explain": "3",

"invoiceType": "增值稅專用發票",
"materialCode": "60010300000001",
"materialName": "裁紙器",
"planQuantity": 1.0,
"price": 3.0,
"remark": "",
"specification": "B41",
"tax": 0.09,
"taxRate": 3,
"tenderId": "b44464dbf0649ba59eba1b7387fec69a",
"totalPrice": 3.0,
"unit": "個"
},
{
"brand": "brand",
"buzzRound": 2,
"cost": 1.96,
"explain": "2",
"invoiceType": "增值稅普通發票",
"materialCode": "60010300000002",
"materialName": "長尾夾",
"planQuantity": 1.0,
"price": 2.0,
"remark": "",
"specification": "32mm",
"tax": 0.04,
"taxRate": 2,
"tenderId": "b44464dbf0649ba59eba1b7387fec69a",
"totalPrice": 2.0,
"unit": "盒"
},
{
"brand": "brand",
"buzzRound": 2,
"cost": 0.99,
"explain": "1",
"invoiceType": "增值稅專用發票",
"materialCode": "60010300000003",
"materialName": "垃圾桶",
"planQuantity": 1.0,
"price": 1.0,
"remark": "",
"specification": "35*35*45cm",
"tax": 0.01,
"taxRate": 1,
"tenderId": "b44464dbf0649ba59eba1b7387fec69a",
"totalPrice": 1.0,
"unit": "個"
},
{
"brand": "brand",
"buzzRound": 1,
"cost": 2079.21,
"explain": "備註",
"invoiceType": "增值稅普通發票",
"materialCode": "60010300000001",
"materialName": "裁紙器",
"planQuantity": 1.0,
"price": 2100.0,
"remark": "",
"specification": "B41",
"tax": 20.79,
"taxRate": 1,
"tenderId": "b44464dbf0649ba59eba1b7387fec69a",
"totalPrice": 2100.0,
"unit": "個"
},
{
"brand": "brand",
"buzzRound": 1,
"cost": 2079.21,
"explain": "備註",
"invoiceType": "增值稅普通發票",
"materialCode": "60010300000002",
"materialName": "長尾夾",
"planQuantity": 1.0,
"price": 2100.0,
"remark": "",
"specification": "32mm",
"tax": 20.79,
"taxRate": 1,
"tenderId": "b44464dbf0649ba59eba1b7387fec69a",
"totalPrice": 2100.0,
"unit": "盒"
},
{
"brand": "brand",
"buzzRound": 1,
"cost": 2079.21,
"explain": "備註",
"invoiceType": "增值稅普通發票",
"materialCode": "60010300000003",
"materialName": "垃圾桶",
"planQuantity": 1.0,
"price": 2100.0,
"remark": "",
"specification": "35*35*45cm",
"tax": 20.79,
"taxRate": 1,
"tenderId": "b44464dbf0649ba59eba1b7387fec69a",
"totalPrice": 2100.0,
"unit": "個"
}
]

這裡我們採用的方法是:首先拆分excel模板,使用POI操作excel複製區域性模板,最後使用easyexcel的多列表組合填充填充,模板如下所示

序號物料編碼物料名稱規格型號品牌/廠家計劃備註單位數量第#輪
發票型別含稅單價(元)含稅總價(元)稅率(%)總稅金(元)不含稅總價(元)備註
{data0.materialCode}{data0.materialName}{data0.specification}{data0.brand}{data0.remark}{data0.unit}{data0.planQuantity}{data#.invoiceType}{data#.price}{data#.totalPrice}{data#.taxRate}{data#.tax}{data#.cost}{data#.explain}

使用POI根據輪數複製data#處模板,並修改模板的資料,修改後的模板如下:

序號物料編碼物料名稱規格型號品牌/廠家計劃備註單位數量第2輪第1輪
型別含稅單價(元)含稅總價(元)稅率(%)總稅金(元)不含稅總價(元)報價備註型別含稅單價(元)含稅總價(元)稅率(%)總稅金(元)不含稅總價(元)備註
{data0.materialCode}{data0.materialName}{data0.specification}{data0.brand}{data0.remark}{data0.unit}{data0.planQuantity}{data2.invoiceType}{data2.price}{data2.totalPrice }{data2.taxRate}{data2.tax}{data2.cost}{data2.explain}{data1.invoiceType}{data1.price}{data1.totalPrice }{data1.taxRate}{data1.tax}{data1.cost}{data1.explain}

核心程式碼如下

try {
			String templateFileName = "C:\\Users\\user\\Desktop\\template.xlsx";

			File fi = new File(templateFileName);
			XSSFWorkbook wb = new XSSFWorkbook(new FileInputStream(fi));
			Sheet sheet = wb.getSheetAt(0);
			//起始列
			int startColumn = 8;
			//每輪列數
			int step = 8;
			//輪數
			int round = 3;
			//輪次
			for (int i = 1; i < round; i++) {
				//行
				for (int j = 0; j < 3; j++) {
					//列
					for (int k = 0; k < step; k++) {

						copyCell(sheet, j, startColumn + k, sheet, j, (i * startColumn) + step + k);

					}
					if (j == 0) {
						continue;
					}
				}
			}


			//更新模板數字
			for (int i = 1; i <= round; i++) {
				//行
				for (int j = 0; j < 3; j++) {

					if (j == 0) {
						Cell cell = getCell(sheet, j, startColumn + ((i - 1) * step));
						cell.setCellValue(cell.getStringCellValue().replace("#", String.valueOf(round - i + 1)));
						//合併單元格
						sheet.addMergedRegion(new CellRangeAddress(
							j, //first row (0-based)
							j, //last row  (0-based)
							startColumn + ((i - 1) * step), //first column (0-based)
							startColumn + ((i - 1) * step) + (step - 1)  //last column  (0-based)
						));
					}

					if (j == 2) {
						//列
						for (int k = 0; k < step; k++) {

							Cell cell = getCell(sheet, j, startColumn + ((i - 1) * step) + k);
							cell.setCellValue(cell.getStringCellValue().replace("#", String.valueOf(round - i + 1)));

						}
					}

				}
			}

			FileOutputStream fileOutputStream = new FileOutputStream("C:\\Users\\user\\Desktop\\" + System.currentTimeMillis() + ".xlsx");
			wb.write(fileOutputStream);
			wb.close();
		} catch (IOException e) {
			e.printStackTrace();
		}


	}


	/**
	 * 從(s1,r1,c1)單元格複製內容到(s2,r2,c2)
	 */
	public static void copyCell(Sheet s1, int r1, int c1, Sheet s2, int r2, int c2) {

		Cell cell = getCell(s2, r2, c2);

		Object obj = getObj(s1, r1, c1);
		if (null == obj) {
			//為空不處理
		} else if (obj instanceof String) {
			cell.setCellValue((String) obj);
		} else if (obj instanceof Date) {
			cell.setCellValue((Date) obj);
		} else if (obj instanceof Double) {
			cell.setCellValue((double) obj);
		} else {
			System.out.println("未處理型別:" + obj.getClass());
		}
	}

	private static Cell getCell(Sheet sheet, int r, int c) {

		Row row = sheet.getRow(r);
		if (row == null) {
			row = sheet.createRow(r);
		}

		Cell cell = row.getCell(c);
		if (cell == null) {
			cell = row.createCell(c);
		}

		return cell;
	}

	private static Object getObj(Sheet sheet, int r, int c) {

		Cell cell = getCell(sheet, r, c);

		if (cell.getCellTypeEnum() == CellType.NUMERIC) {
			if (HSSFDateUtil.isCellDateFormatted(cell)) {
				return cell.getDateCellValue();
			} else {
				return cell.getNumericCellValue();
			}
		} else if (cell.getCellTypeEnum() == CellType.STRING) {
			return cell.getStringCellValue();
		} else if (cell.getCellTypeEnum() == CellType.BLANK) {
			return null;
		} else if (cell.getCellTypeEnum() == CellType.FORMULA) {
			if (cell.getCachedFormulaResultTypeEnum() == CellType.NUMERIC) {
				return cell.getNumericCellValue();
			} else {
				return cell.getStringCellValue();
			}
		} else {
			System.out.println("(" + r + "," + c + ")未處理型別:" + cell.getCellTypeEnum());
		}

		return "";
	}

以上使用POI工具將資料模板製作成功,還需要調整資料結構使其對應模板中的data#資料,調整後的資料結構如下:其中key標識輪數 ,陣列則為需要渲染的模板資料, key=0 表示基本資訊

{
"0": [
{
"brand": "brand",
"materialCode": "60010300000001",
"materialName": "裁紙器",
"planQuantity": 1.0,
"remark": "",
"specification": "B41",
"unit": "個"
},
{
"brand": "brand",
"materialCode": "60010300000002",
"materialName": "長尾夾",
"planQuantity": 1.0,
"remark": "",
"specification": "32mm",
"unit": "盒"
},
{
"brand": "brand",
"materialCode": "60010300000003",
"materialName": "垃圾桶",
"planQuantity": 1.0,
"remark": "",
"specification": "35*35*45cm",
"unit": "個"
}
],
"1": [
{
"buzzRound": 1,
"cost": 2079.21,
"explain": "報價備註",
"invoiceType": "增值稅普通發票",
"price": 2100.0,
"tax": 20.79,
"taxRate": 1,
"totalPrice": 2100.0
},
{
"buzzRound": 1,
"cost": 2079.21,
"explain": "報價備註",
"invoiceType": "增值稅普通發票",
"price": 2100.0,
"tax": 20.79,
"taxRate": 1,
"totalPrice": 2100.0
},
{
"buzzRound": 1,
"cost": 2079.21,
"explain": "報價備註",
"invoiceType": "增值稅普通發票",
"price": 2100.0,
"tax": 20.79,
"taxRate": 1,
"totalPrice": 2100.0
}
],
"2": [
{
"buzzRound": 2,
"cost": 2.91,
"explain": "3",
"invoiceType": "增值稅專用發票",
"price": 3.0,
"tax": 0.09,
"taxRate": 3,
"totalPrice": 3.0
},
{
"buzzRound": 2,
"cost": 1.96,
"explain": "2",
"invoiceType": "增值稅普通發票",
"price": 2.0,
"tax": 0.04,
"taxRate": 2,
"totalPrice": 2.0
},
{
"buzzRound": 2,
"cost": 0.99,
"explain": "1",
"invoiceType": "增值稅專用發票",
"price": 1.0,
"tax": 0.01,
"taxRate": 1,
"totalPrice": 1.0
}
]
}

最後根據easyexcel的 :多列表組合填充填充 填充資料 核心程式碼如下:

ExcelWriter excelWriter = EasyExcel.write(byteArrayOutputStream).withTemplate(inputStream).build();

			WriteSheet writeSheet = EasyExcel.writerSheet().build();

			bidMaterialHistory.forEach((k, v) -> {

				excelWriter.fill(new FillWrapper("data" + k, v), writeSheet);

			});
			excelWriter.finish();