mirror of
http://git.pig4cloud.com/pig/pigx.git
synced 2026-11-12 21:49:01 +08:00
✨ Introducing new features. closed #3045 【新特性】common-excel 模块
This commit is contained in:
1 parent
109dc634e6
commit
27e967481c
31 files changed
+2596
No files matched your search
@@ -0,0 +1,97 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
~
|
||||
~ Copyright (c) 2018-2025, lengleng All rights reserved.
|
||||
~
|
||||
~ Redistribution and use in source and binary forms, with or without
|
||||
~ modification, are permitted provided that the following conditions are met:
|
||||
~
|
||||
~ Redistributions of source code must retain the above copyright notice,
|
||||
~ this list of conditions and the following disclaimer.
|
||||
~ Redistributions in binary form must reproduce the above copyright
|
||||
~ notice, this list of conditions and the following disclaimer in the
|
||||
~ documentation and/or other materials provided with the distribution.
|
||||
~ Neither the name of the pig4cloud.com developer nor the names of its
|
||||
~ contributors may be used to endorse or promote products derived from
|
||||
~ this software without specific prior written permission.
|
||||
~ Author: lengleng (wangiegie@gmail.com)
|
||||
~
|
||||
-->
|
||||
|
||||
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>com.pig4cloud</groupId>
|
||||
<artifactId>pigx-common</artifactId>
|
||||
<version>4.1.0</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>pigx-common-excel</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<description>pigx excel 操作</description>
|
||||
|
||||
<properties>
|
||||
<poi.version>4.1.2</poi.version>
|
||||
<easypoi.version>2.2.10</easypoi.version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-validation</artifactId>
|
||||
</dependency>
|
||||
<!--引入AOP依赖-->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-aop</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.alibaba</groupId>
|
||||
<artifactId>easyexcel</artifactId>
|
||||
<version>${easypoi.version}</version>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>org.apache.poi</groupId>
|
||||
<artifactId>poi</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>org.apache.poi</groupId>
|
||||
<artifactId>poi-ooxml</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>org.apache.poi</groupId>
|
||||
<artifactId>poi-ooxml-schemas</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<!-- 使用 POI 4.x 解决 issue #1 支持java11-->
|
||||
<dependency>
|
||||
<groupId>org.apache.poi</groupId>
|
||||
<artifactId>poi-ooxml</artifactId>
|
||||
<version>${poi.version}</version>
|
||||
</dependency>
|
||||
<!-- lombok -->
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<!-- test -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>jakarta.servlet</groupId>
|
||||
<artifactId>jakarta.servlet-api</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
+526
@@ -0,0 +1,526 @@
|
||||
package com.alibaba.excel.context;
|
||||
|
||||
import com.alibaba.excel.enums.WriteTypeEnum;
|
||||
import com.alibaba.excel.exception.ExcelGenerateException;
|
||||
import com.alibaba.excel.metadata.CellData;
|
||||
import com.alibaba.excel.metadata.Head;
|
||||
import com.alibaba.excel.support.ExcelTypeEnum;
|
||||
import com.alibaba.excel.util.*;
|
||||
import com.alibaba.excel.write.metadata.WriteSheet;
|
||||
import com.alibaba.excel.write.metadata.WriteTable;
|
||||
import com.alibaba.excel.write.metadata.WriteWorkbook;
|
||||
import com.alibaba.excel.write.metadata.holder.WriteHolder;
|
||||
import com.alibaba.excel.write.metadata.holder.WriteSheetHolder;
|
||||
import com.alibaba.excel.write.metadata.holder.WriteTableHolder;
|
||||
import com.alibaba.excel.write.metadata.holder.WriteWorkbookHolder;
|
||||
import com.alibaba.excel.write.property.ExcelWriteHeadProperty;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.poi.hssf.record.crypto.Biff8EncryptionKey;
|
||||
import org.apache.poi.openxml4j.opc.OPCPackage;
|
||||
import org.apache.poi.openxml4j.opc.PackageAccess;
|
||||
import org.apache.poi.poifs.crypt.EncryptionInfo;
|
||||
import org.apache.poi.poifs.crypt.EncryptionMode;
|
||||
import org.apache.poi.poifs.crypt.Encryptor;
|
||||
import org.apache.poi.poifs.filesystem.POIFSFileSystem;
|
||||
import org.apache.poi.ss.usermodel.Cell;
|
||||
import org.apache.poi.ss.usermodel.Row;
|
||||
import org.apache.poi.ss.usermodel.Sheet;
|
||||
import org.apache.poi.ss.usermodel.Workbook;
|
||||
import org.apache.poi.ss.util.CellRangeAddress;
|
||||
import org.apache.poi.xssf.streaming.SXSSFWorkbook;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* A context is the main anchorage point of a excel writer.
|
||||
*
|
||||
* @author jipengfei
|
||||
*/
|
||||
@Slf4j
|
||||
public class WriteContextImpl implements WriteContext {
|
||||
|
||||
static {
|
||||
log.debug("======[WriteContextImpl] 修复版被加载了=======");
|
||||
}
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(WriteContextImpl.class);
|
||||
|
||||
/**
|
||||
* The Workbook currently written
|
||||
*/
|
||||
private WriteWorkbookHolder writeWorkbookHolder;
|
||||
|
||||
/**
|
||||
* Current sheet holder
|
||||
*/
|
||||
private WriteSheetHolder writeSheetHolder;
|
||||
|
||||
/**
|
||||
* The table currently written
|
||||
*/
|
||||
private WriteTableHolder writeTableHolder;
|
||||
|
||||
/**
|
||||
* Configuration of currently operated cell
|
||||
*/
|
||||
private WriteHolder currentWriteHolder;
|
||||
|
||||
/**
|
||||
* Prevent multiple shutdowns
|
||||
*/
|
||||
private boolean finished = false;
|
||||
|
||||
public WriteContextImpl(WriteWorkbook writeWorkbook) {
|
||||
if (writeWorkbook == null) {
|
||||
throw new IllegalArgumentException("Workbook argument cannot be null");
|
||||
}
|
||||
if (LOGGER.isDebugEnabled()) {
|
||||
LOGGER.debug("Begin to Initialization 'WriteContextImpl'");
|
||||
}
|
||||
initCurrentWorkbookHolder(writeWorkbook);
|
||||
WriteHandlerUtils.beforeWorkbookCreate(this);
|
||||
try {
|
||||
WorkBookUtil.createWorkBook(writeWorkbookHolder);
|
||||
}
|
||||
catch (Exception e) {
|
||||
throw new ExcelGenerateException("Create workbook failure", e);
|
||||
}
|
||||
WriteHandlerUtils.afterWorkbookCreate(this);
|
||||
if (LOGGER.isDebugEnabled()) {
|
||||
LOGGER.debug("Initialization 'WriteContextImpl' complete");
|
||||
}
|
||||
}
|
||||
|
||||
private void initCurrentWorkbookHolder(WriteWorkbook writeWorkbook) {
|
||||
writeWorkbookHolder = new WriteWorkbookHolder(writeWorkbook);
|
||||
currentWriteHolder = writeWorkbookHolder;
|
||||
if (LOGGER.isDebugEnabled()) {
|
||||
LOGGER.debug("CurrentConfiguration is writeWorkbookHolder");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param writeSheet
|
||||
*/
|
||||
@Override
|
||||
public void currentSheet(WriteSheet writeSheet, WriteTypeEnum writeType) {
|
||||
if (writeSheet == null) {
|
||||
throw new IllegalArgumentException("Sheet argument cannot be null");
|
||||
}
|
||||
if (selectSheetFromCache(writeSheet)) {
|
||||
return;
|
||||
}
|
||||
|
||||
initCurrentSheetHolder(writeSheet);
|
||||
|
||||
// Workbook handler need to supplementary execution
|
||||
WriteHandlerUtils.beforeWorkbookCreate(this, true);
|
||||
WriteHandlerUtils.afterWorkbookCreate(this, true);
|
||||
|
||||
// Initialization current sheet
|
||||
initSheet(writeType);
|
||||
}
|
||||
|
||||
private boolean selectSheetFromCache(WriteSheet writeSheet) {
|
||||
writeSheetHolder = null;
|
||||
Integer sheetNo = writeSheet.getSheetNo();
|
||||
if (sheetNo == null && StringUtils.isEmpty(writeSheet.getSheetName())) {
|
||||
sheetNo = 0;
|
||||
}
|
||||
if (sheetNo != null) {
|
||||
writeSheetHolder = writeWorkbookHolder.getHasBeenInitializedSheetIndexMap().get(sheetNo);
|
||||
}
|
||||
if (writeSheetHolder == null && !StringUtils.isEmpty(writeSheet.getSheetName())) {
|
||||
writeSheetHolder = writeWorkbookHolder.getHasBeenInitializedSheetNameMap().get(writeSheet.getSheetName());
|
||||
}
|
||||
if (writeSheetHolder == null) {
|
||||
return false;
|
||||
}
|
||||
if (LOGGER.isDebugEnabled()) {
|
||||
LOGGER.debug("Sheet:{} is already existed", writeSheet.getSheetNo());
|
||||
}
|
||||
writeSheetHolder.setNewInitialization(Boolean.FALSE);
|
||||
writeTableHolder = null;
|
||||
currentWriteHolder = writeSheetHolder;
|
||||
if (LOGGER.isDebugEnabled()) {
|
||||
LOGGER.debug("CurrentConfiguration is writeSheetHolder");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private void initCurrentSheetHolder(WriteSheet writeSheet) {
|
||||
writeSheetHolder = new WriteSheetHolder(writeSheet, writeWorkbookHolder);
|
||||
writeTableHolder = null;
|
||||
currentWriteHolder = writeSheetHolder;
|
||||
if (LOGGER.isDebugEnabled()) {
|
||||
LOGGER.debug("CurrentConfiguration is writeSheetHolder");
|
||||
}
|
||||
}
|
||||
|
||||
private void initSheet(WriteTypeEnum writeType) {
|
||||
WriteHandlerUtils.beforeSheetCreate(this);
|
||||
Sheet currentSheet;
|
||||
try {
|
||||
if (writeSheetHolder.getSheetNo() != null) {
|
||||
// When the add default sort order of appearance
|
||||
if (WriteTypeEnum.ADD.equals(writeType) && writeWorkbookHolder.getTempTemplateInputStream() == null) {
|
||||
currentSheet = createSheet();
|
||||
}
|
||||
else {
|
||||
currentSheet = writeWorkbookHolder.getWorkbook().getSheetAt(writeSheetHolder.getSheetNo());
|
||||
writeSheetHolder.setCachedSheet(
|
||||
writeWorkbookHolder.getCachedWorkbook().getSheetAt(writeSheetHolder.getSheetNo()));
|
||||
}
|
||||
}
|
||||
else {
|
||||
// sheet name must not null
|
||||
currentSheet = writeWorkbookHolder.getWorkbook().getSheet(writeSheetHolder.getSheetName());
|
||||
writeSheetHolder.setCachedSheet(
|
||||
writeWorkbookHolder.getCachedWorkbook().getSheet(writeSheetHolder.getSheetName()));
|
||||
}
|
||||
}
|
||||
catch (Exception e) {
|
||||
currentSheet = createSheet();
|
||||
}
|
||||
if (currentSheet == null) {
|
||||
currentSheet = createSheet();
|
||||
}
|
||||
writeSheetHolder.setSheet(currentSheet);
|
||||
WriteHandlerUtils.afterSheetCreate(this);
|
||||
if (WriteTypeEnum.ADD.equals(writeType)) {
|
||||
// Initialization head
|
||||
initHead(writeSheetHolder.excelWriteHeadProperty());
|
||||
}
|
||||
writeWorkbookHolder.getHasBeenInitializedSheetIndexMap().put(writeSheetHolder.getSheetNo(), writeSheetHolder);
|
||||
writeWorkbookHolder.getHasBeenInitializedSheetNameMap().put(writeSheetHolder.getSheetName(), writeSheetHolder);
|
||||
}
|
||||
|
||||
private Sheet createSheet() {
|
||||
if (LOGGER.isDebugEnabled()) {
|
||||
LOGGER.debug("Can not find sheet:{} ,now create it", writeSheetHolder.getSheetNo());
|
||||
}
|
||||
if (StringUtils.isEmpty(writeSheetHolder.getSheetName())) {
|
||||
writeSheetHolder.setSheetName(writeSheetHolder.getSheetNo().toString());
|
||||
}
|
||||
Sheet currentSheet = WorkBookUtil.createSheet(writeWorkbookHolder.getWorkbook(),
|
||||
writeSheetHolder.getSheetName());
|
||||
writeSheetHolder.setCachedSheet(currentSheet);
|
||||
return currentSheet;
|
||||
}
|
||||
|
||||
public void initHead(ExcelWriteHeadProperty excelWriteHeadProperty) {
|
||||
if (!currentWriteHolder.needHead() || !currentWriteHolder.excelWriteHeadProperty().hasHead()) {
|
||||
return;
|
||||
}
|
||||
int newRowIndex = writeSheetHolder.getNewRowIndexAndStartDoWrite();
|
||||
newRowIndex += currentWriteHolder.relativeHeadRowIndex();
|
||||
// Combined head
|
||||
if (currentWriteHolder.automaticMergeHead()) {
|
||||
addMergedRegionToCurrentSheet(excelWriteHeadProperty, newRowIndex);
|
||||
}
|
||||
for (int relativeRowIndex = 0,
|
||||
i = newRowIndex; i < excelWriteHeadProperty.getHeadRowNumber() + newRowIndex; i++, relativeRowIndex++) {
|
||||
WriteHandlerUtils.beforeRowCreate(this, newRowIndex, relativeRowIndex, Boolean.TRUE);
|
||||
Row row = WorkBookUtil.createRow(writeSheetHolder.getSheet(), i);
|
||||
WriteHandlerUtils.afterRowCreate(this, row, relativeRowIndex, Boolean.TRUE);
|
||||
addOneRowOfHeadDataToExcel(row, excelWriteHeadProperty.getHeadMap(), relativeRowIndex);
|
||||
WriteHandlerUtils.afterRowDispose(this, row, relativeRowIndex, Boolean.TRUE);
|
||||
}
|
||||
}
|
||||
|
||||
private void addMergedRegionToCurrentSheet(ExcelWriteHeadProperty excelWriteHeadProperty, int rowIndex) {
|
||||
for (com.alibaba.excel.metadata.CellRange cellRangeModel : excelWriteHeadProperty.headCellRangeList()) {
|
||||
writeSheetHolder.getSheet()
|
||||
.addMergedRegionUnsafe(new CellRangeAddress(cellRangeModel.getFirstRow() + rowIndex,
|
||||
cellRangeModel.getLastRow() + rowIndex, cellRangeModel.getFirstCol(),
|
||||
cellRangeModel.getLastCol()));
|
||||
}
|
||||
}
|
||||
|
||||
private void addOneRowOfHeadDataToExcel(Row row, Map<Integer, Head> headMap, int relativeRowIndex) {
|
||||
for (Map.Entry<Integer, Head> entry : headMap.entrySet()) {
|
||||
Head head = entry.getValue();
|
||||
int columnIndex = entry.getKey();
|
||||
WriteHandlerUtils.beforeCellCreate(this, row, head, columnIndex, relativeRowIndex, Boolean.TRUE);
|
||||
Cell cell = row.createCell(columnIndex);
|
||||
WriteHandlerUtils.afterCellCreate(this, cell, head, relativeRowIndex, Boolean.TRUE);
|
||||
cell.setCellValue(head.getHeadNameList().get(relativeRowIndex));
|
||||
WriteHandlerUtils.afterCellDispose(this, (CellData) null, cell, head, relativeRowIndex, Boolean.TRUE);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void currentTable(WriteTable writeTable) {
|
||||
if (writeTable == null) {
|
||||
return;
|
||||
}
|
||||
if (writeTable.getTableNo() == null || writeTable.getTableNo() <= 0) {
|
||||
writeTable.setTableNo(0);
|
||||
}
|
||||
if (writeSheetHolder.getHasBeenInitializedTable().containsKey(writeTable.getTableNo())) {
|
||||
if (LOGGER.isDebugEnabled()) {
|
||||
LOGGER.debug("Table:{} is already existed", writeTable.getTableNo());
|
||||
}
|
||||
writeTableHolder = writeSheetHolder.getHasBeenInitializedTable().get(writeTable.getTableNo());
|
||||
writeTableHolder.setNewInitialization(Boolean.FALSE);
|
||||
currentWriteHolder = writeTableHolder;
|
||||
if (LOGGER.isDebugEnabled()) {
|
||||
LOGGER.debug("CurrentConfiguration is writeTableHolder");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
initCurrentTableHolder(writeTable);
|
||||
|
||||
// Workbook and sheet handler need to supplementary execution
|
||||
WriteHandlerUtils.beforeWorkbookCreate(this, true);
|
||||
WriteHandlerUtils.afterWorkbookCreate(this, true);
|
||||
WriteHandlerUtils.beforeSheetCreate(this, true);
|
||||
WriteHandlerUtils.afterSheetCreate(this, true);
|
||||
|
||||
initHead(writeTableHolder.excelWriteHeadProperty());
|
||||
}
|
||||
|
||||
private void initCurrentTableHolder(WriteTable writeTable) {
|
||||
writeTableHolder = new WriteTableHolder(writeTable, writeSheetHolder, writeWorkbookHolder);
|
||||
writeSheetHolder.getHasBeenInitializedTable().put(writeTable.getTableNo(), writeTableHolder);
|
||||
currentWriteHolder = writeTableHolder;
|
||||
if (LOGGER.isDebugEnabled()) {
|
||||
LOGGER.debug("CurrentConfiguration is writeTableHolder");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public WriteWorkbookHolder writeWorkbookHolder() {
|
||||
return writeWorkbookHolder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public WriteSheetHolder writeSheetHolder() {
|
||||
return writeSheetHolder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public WriteTableHolder writeTableHolder() {
|
||||
return writeTableHolder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public WriteHolder currentWriteHolder() {
|
||||
return currentWriteHolder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void finish(boolean onException) {
|
||||
if (finished) {
|
||||
return;
|
||||
}
|
||||
finished = true;
|
||||
WriteHandlerUtils.afterWorkbookDispose(this);
|
||||
if (writeWorkbookHolder == null) {
|
||||
return;
|
||||
}
|
||||
Throwable throwable = null;
|
||||
boolean isOutputStreamEncrypt = false;
|
||||
// Determine if you need to write excel
|
||||
boolean writeExcel = !onException;
|
||||
if (writeWorkbookHolder.getWriteExcelOnException()) {
|
||||
writeExcel = Boolean.TRUE;
|
||||
}
|
||||
// No data is written if an exception is thrown
|
||||
if (writeExcel) {
|
||||
try {
|
||||
isOutputStreamEncrypt = doOutputStreamEncrypt07();
|
||||
}
|
||||
catch (Throwable t) {
|
||||
throwable = t;
|
||||
}
|
||||
}
|
||||
if (!isOutputStreamEncrypt) {
|
||||
try {
|
||||
if (writeExcel) {
|
||||
writeWorkbookHolder.getWorkbook().write(writeWorkbookHolder.getOutputStream());
|
||||
}
|
||||
writeWorkbookHolder.getWorkbook().close();
|
||||
}
|
||||
catch (Throwable t) {
|
||||
throwable = t;
|
||||
}
|
||||
}
|
||||
try {
|
||||
Workbook workbook = writeWorkbookHolder.getWorkbook();
|
||||
if (workbook instanceof SXSSFWorkbook) {
|
||||
((SXSSFWorkbook) workbook).dispose();
|
||||
}
|
||||
}
|
||||
catch (Throwable t) {
|
||||
throwable = t;
|
||||
}
|
||||
try {
|
||||
if (writeWorkbookHolder.getAutoCloseStream() && writeWorkbookHolder.getOutputStream() != null) {
|
||||
writeWorkbookHolder.getOutputStream().close();
|
||||
}
|
||||
}
|
||||
catch (Throwable t) {
|
||||
throwable = t;
|
||||
}
|
||||
if (writeExcel && !isOutputStreamEncrypt) {
|
||||
try {
|
||||
doFileEncrypt07();
|
||||
}
|
||||
catch (Throwable t) {
|
||||
throwable = t;
|
||||
}
|
||||
}
|
||||
try {
|
||||
if (writeWorkbookHolder.getTempTemplateInputStream() != null) {
|
||||
writeWorkbookHolder.getTempTemplateInputStream().close();
|
||||
}
|
||||
}
|
||||
catch (Throwable t) {
|
||||
throwable = t;
|
||||
}
|
||||
clearEncrypt03();
|
||||
removeThreadLocalCache();
|
||||
if (throwable != null) {
|
||||
throw new ExcelGenerateException("Can not close IO.", throwable);
|
||||
}
|
||||
if (LOGGER.isDebugEnabled()) {
|
||||
LOGGER.debug("Finished write.");
|
||||
}
|
||||
}
|
||||
|
||||
private void removeThreadLocalCache() {
|
||||
NumberDataFormatterUtils.removeThreadLocalCache();
|
||||
DateUtils.removeThreadLocalCache();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Sheet getCurrentSheet() {
|
||||
return writeSheetHolder.getSheet();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean needHead() {
|
||||
return writeSheetHolder.needHead();
|
||||
}
|
||||
|
||||
@Override
|
||||
public OutputStream getOutputStream() {
|
||||
return writeWorkbookHolder.getOutputStream();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Workbook getWorkbook() {
|
||||
return writeWorkbookHolder.getWorkbook();
|
||||
}
|
||||
|
||||
private void clearEncrypt03() {
|
||||
if (StringUtils.isEmpty(writeWorkbookHolder.getPassword())
|
||||
|| !ExcelTypeEnum.XLS.equals(writeWorkbookHolder.getExcelType())) {
|
||||
return;
|
||||
}
|
||||
Biff8EncryptionKey.setCurrentUserPassword(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* To encrypt
|
||||
*/
|
||||
private boolean doOutputStreamEncrypt07() throws Exception {
|
||||
if (StringUtils.isEmpty(writeWorkbookHolder.getPassword())
|
||||
|| !ExcelTypeEnum.XLSX.equals(writeWorkbookHolder.getExcelType())) {
|
||||
return false;
|
||||
}
|
||||
if (writeWorkbookHolder.getFile() != null) {
|
||||
return false;
|
||||
}
|
||||
File tempXlsx = FileUtils.createTmpFile(UUID.randomUUID().toString() + ".xlsx");
|
||||
FileOutputStream tempFileOutputStream = new FileOutputStream(tempXlsx);
|
||||
try {
|
||||
writeWorkbookHolder.getWorkbook().write(tempFileOutputStream);
|
||||
}
|
||||
finally {
|
||||
try {
|
||||
writeWorkbookHolder.getWorkbook().close();
|
||||
tempFileOutputStream.close();
|
||||
}
|
||||
catch (Exception e) {
|
||||
if (!tempXlsx.delete()) {
|
||||
throw new ExcelGenerateException("Can not delete temp File!");
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
POIFSFileSystem fileSystem = null;
|
||||
try {
|
||||
fileSystem = openFileSystemAndEncrypt(tempXlsx);
|
||||
fileSystem.writeFilesystem(writeWorkbookHolder.getOutputStream());
|
||||
}
|
||||
finally {
|
||||
if (fileSystem != null) {
|
||||
fileSystem.close();
|
||||
}
|
||||
if (!tempXlsx.delete()) {
|
||||
throw new ExcelGenerateException("Can not delete temp File!");
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* To encrypt
|
||||
*/
|
||||
private void doFileEncrypt07() throws Exception {
|
||||
if (StringUtils.isEmpty(writeWorkbookHolder.getPassword())
|
||||
|| !ExcelTypeEnum.XLSX.equals(writeWorkbookHolder.getExcelType())) {
|
||||
return;
|
||||
}
|
||||
if (writeWorkbookHolder.getFile() == null) {
|
||||
return;
|
||||
}
|
||||
FileOutputStream fileOutputStream = null;
|
||||
POIFSFileSystem fileSystem = null;
|
||||
try {
|
||||
fileSystem = openFileSystemAndEncrypt(writeWorkbookHolder.getFile());
|
||||
fileOutputStream = new FileOutputStream(writeWorkbookHolder.getFile());
|
||||
fileSystem.writeFilesystem(fileOutputStream);
|
||||
}
|
||||
finally {
|
||||
if (fileOutputStream != null) {
|
||||
fileOutputStream.close();
|
||||
}
|
||||
if (fileSystem != null) {
|
||||
fileSystem.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private POIFSFileSystem openFileSystemAndEncrypt(File file) throws Exception {
|
||||
POIFSFileSystem fileSystem = new POIFSFileSystem();
|
||||
Encryptor encryptor = new EncryptionInfo(EncryptionMode.standard).getEncryptor();
|
||||
encryptor.confirmPassword(writeWorkbookHolder.getPassword());
|
||||
OPCPackage opcPackage = null;
|
||||
OutputStream outputStream = null;
|
||||
try {
|
||||
opcPackage = OPCPackage.open(file, PackageAccess.READ_WRITE);
|
||||
outputStream = encryptor.getDataStream(fileSystem);
|
||||
opcPackage.save(outputStream);
|
||||
}
|
||||
finally {
|
||||
if (opcPackage != null) {
|
||||
opcPackage.close();
|
||||
}
|
||||
if (outputStream != null) {
|
||||
outputStream.close();
|
||||
}
|
||||
}
|
||||
return fileSystem;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,221 @@
|
||||
package com.alibaba.excel.util;
|
||||
|
||||
import com.alibaba.excel.annotation.ExcelIgnore;
|
||||
import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
|
||||
import com.alibaba.excel.annotation.ExcelProperty;
|
||||
import com.alibaba.excel.exception.ExcelCommonException;
|
||||
import com.alibaba.excel.metadata.BaseRowModel;
|
||||
import com.alibaba.excel.metadata.Holder;
|
||||
import com.alibaba.excel.write.metadata.holder.WriteHolder;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import java.lang.ref.SoftReference;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
* Class utils
|
||||
*
|
||||
* @author Jiaju Zhuang
|
||||
**/
|
||||
@Slf4j
|
||||
public class ClassUtils {
|
||||
|
||||
static {
|
||||
log.debug("======[ClassUtils] 修复版被加载了=======");
|
||||
}
|
||||
|
||||
private static final Map<Class, SoftReference<FieldCache>> FIELD_CACHE = new ConcurrentHashMap<Class, SoftReference<FieldCache>>();
|
||||
|
||||
public static void declaredFields(Class clazz, Map<Integer, Field> sortedAllFiledMap,
|
||||
Map<Integer, Field> indexFiledMap, Map<String, Field> ignoreMap, Boolean convertAllFiled,
|
||||
Boolean needIgnore, Holder holder) {
|
||||
FieldCache fieldCache = getFieldCache(clazz, convertAllFiled);
|
||||
if (fieldCache == null) {
|
||||
return;
|
||||
}
|
||||
if (ignoreMap != null) {
|
||||
ignoreMap.putAll(fieldCache.getIgnoreMap());
|
||||
}
|
||||
Map<Integer, Field> tempIndexFildMap = indexFiledMap;
|
||||
if (tempIndexFildMap == null) {
|
||||
tempIndexFildMap = new TreeMap<Integer, Field>();
|
||||
}
|
||||
tempIndexFildMap.putAll(fieldCache.getIndexFiledMap());
|
||||
|
||||
Map<Integer, Field> originSortedAllFiledMap = fieldCache.getSortedAllFiledMap();
|
||||
if (!needIgnore) {
|
||||
sortedAllFiledMap.putAll(originSortedAllFiledMap);
|
||||
return;
|
||||
}
|
||||
|
||||
// 获取到属性字段的最大index
|
||||
int maxIndex = -1;
|
||||
for (Integer filedIndex : originSortedAllFiledMap.keySet()) {
|
||||
maxIndex = Math.max(filedIndex, maxIndex);
|
||||
}
|
||||
// 被忽略的属性数量
|
||||
int ignoreNum = 0;
|
||||
// 当有属性被忽略时,需要将其后面的所有属性 index 前移
|
||||
for (int index = 0; index <= maxIndex; index++) {
|
||||
Field field = originSortedAllFiledMap.get(index);
|
||||
String name = field == null ? null : field.getName();
|
||||
if (((WriteHolder) holder).ignore(name, index)) {
|
||||
if (ignoreMap != null && name != null) {
|
||||
ignoreMap.put(name, field);
|
||||
}
|
||||
tempIndexFildMap.remove(index);
|
||||
ignoreNum++;
|
||||
}
|
||||
else if (field != null) {
|
||||
int finalIndex = index - ignoreNum;
|
||||
sortedAllFiledMap.put(finalIndex, field);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void declaredFields(Class clazz, Map<Integer, Field> sortedAllFiledMap, Boolean convertAllFiled,
|
||||
Boolean needIgnore, WriteHolder writeHolder) {
|
||||
declaredFields(clazz, sortedAllFiledMap, null, null, convertAllFiled, needIgnore, writeHolder);
|
||||
}
|
||||
|
||||
private static FieldCache getFieldCache(Class clazz, Boolean convertAllFiled) {
|
||||
if (clazz == null) {
|
||||
return null;
|
||||
}
|
||||
SoftReference<FieldCache> fieldCacheSoftReference = FIELD_CACHE.get(clazz);
|
||||
if (fieldCacheSoftReference != null && fieldCacheSoftReference.get() != null) {
|
||||
return fieldCacheSoftReference.get();
|
||||
}
|
||||
synchronized (clazz) {
|
||||
fieldCacheSoftReference = FIELD_CACHE.get(clazz);
|
||||
if (fieldCacheSoftReference != null && fieldCacheSoftReference.get() != null) {
|
||||
return fieldCacheSoftReference.get();
|
||||
}
|
||||
declaredFields(clazz, convertAllFiled);
|
||||
}
|
||||
return FIELD_CACHE.get(clazz).get();
|
||||
}
|
||||
|
||||
private static void declaredFields(Class clazz, Boolean convertAllFiled) {
|
||||
List<Field> tempFieldList = new ArrayList<Field>();
|
||||
Class tempClass = clazz;
|
||||
// When the parent class is null, it indicates that the parent class (Object
|
||||
// class) has reached the top
|
||||
// level.
|
||||
while (tempClass != null && tempClass != BaseRowModel.class) {
|
||||
Collections.addAll(tempFieldList, tempClass.getDeclaredFields());
|
||||
// Get the parent class and give it to yourself
|
||||
tempClass = tempClass.getSuperclass();
|
||||
}
|
||||
// Screening of field
|
||||
Map<Integer, List<Field>> orderFiledMap = new TreeMap<Integer, List<Field>>();
|
||||
Map<Integer, Field> indexFiledMap = new TreeMap<Integer, Field>();
|
||||
Map<String, Field> ignoreMap = new HashMap<String, Field>(16);
|
||||
|
||||
ExcelIgnoreUnannotated excelIgnoreUnannotated = (ExcelIgnoreUnannotated) clazz
|
||||
.getAnnotation(ExcelIgnoreUnannotated.class);
|
||||
for (Field field : tempFieldList) {
|
||||
declaredOneField(field, orderFiledMap, indexFiledMap, ignoreMap, excelIgnoreUnannotated, convertAllFiled);
|
||||
}
|
||||
FIELD_CACHE.put(clazz, new SoftReference<FieldCache>(
|
||||
new FieldCache(buildSortedAllFiledMap(orderFiledMap, indexFiledMap), indexFiledMap, ignoreMap)));
|
||||
}
|
||||
|
||||
private static Map<Integer, Field> buildSortedAllFiledMap(Map<Integer, List<Field>> orderFiledMap,
|
||||
Map<Integer, Field> indexFiledMap) {
|
||||
|
||||
Map<Integer, Field> sortedAllFiledMap = new HashMap<Integer, Field>(
|
||||
(orderFiledMap.size() + indexFiledMap.size()) * 4 / 3 + 1);
|
||||
|
||||
Map<Integer, Field> tempIndexFiledMap = new HashMap<Integer, Field>(indexFiledMap);
|
||||
int index = 0;
|
||||
for (List<Field> fieldList : orderFiledMap.values()) {
|
||||
for (Field field : fieldList) {
|
||||
while (tempIndexFiledMap.containsKey(index)) {
|
||||
sortedAllFiledMap.put(index, tempIndexFiledMap.get(index));
|
||||
tempIndexFiledMap.remove(index);
|
||||
index++;
|
||||
}
|
||||
sortedAllFiledMap.put(index, field);
|
||||
index++;
|
||||
}
|
||||
}
|
||||
sortedAllFiledMap.putAll(tempIndexFiledMap);
|
||||
return sortedAllFiledMap;
|
||||
}
|
||||
|
||||
private static void declaredOneField(Field field, Map<Integer, List<Field>> orderFiledMap,
|
||||
Map<Integer, Field> indexFiledMap, Map<String, Field> ignoreMap,
|
||||
ExcelIgnoreUnannotated excelIgnoreUnannotated, Boolean convertAllFiled) {
|
||||
ExcelIgnore excelIgnore = field.getAnnotation(ExcelIgnore.class);
|
||||
if (excelIgnore != null) {
|
||||
ignoreMap.put(field.getName(), field);
|
||||
return;
|
||||
}
|
||||
ExcelProperty excelProperty = field.getAnnotation(ExcelProperty.class);
|
||||
boolean noExcelProperty = excelProperty == null
|
||||
&& ((convertAllFiled != null && !convertAllFiled) || excelIgnoreUnannotated != null);
|
||||
if (noExcelProperty) {
|
||||
ignoreMap.put(field.getName(), field);
|
||||
return;
|
||||
}
|
||||
boolean isStaticFinalOrTransient = (Modifier.isStatic(field.getModifiers())
|
||||
&& Modifier.isFinal(field.getModifiers())) || Modifier.isTransient(field.getModifiers());
|
||||
if (excelProperty == null && isStaticFinalOrTransient) {
|
||||
ignoreMap.put(field.getName(), field);
|
||||
return;
|
||||
}
|
||||
if (excelProperty != null && excelProperty.index() >= 0) {
|
||||
if (indexFiledMap.containsKey(excelProperty.index())) {
|
||||
throw new ExcelCommonException("The index of '" + indexFiledMap.get(excelProperty.index()).getName()
|
||||
+ "' and '" + field.getName() + "' must be inconsistent");
|
||||
}
|
||||
indexFiledMap.put(excelProperty.index(), field);
|
||||
return;
|
||||
}
|
||||
|
||||
int order = Integer.MAX_VALUE;
|
||||
if (excelProperty != null) {
|
||||
order = excelProperty.order();
|
||||
}
|
||||
List<Field> orderFiledList = orderFiledMap.get(order);
|
||||
if (orderFiledList == null) {
|
||||
orderFiledList = new ArrayList<Field>();
|
||||
orderFiledMap.put(order, orderFiledList);
|
||||
}
|
||||
orderFiledList.add(field);
|
||||
}
|
||||
|
||||
private static class FieldCache {
|
||||
|
||||
private Map<Integer, Field> sortedAllFiledMap;
|
||||
|
||||
private Map<Integer, Field> indexFiledMap;
|
||||
|
||||
private Map<String, Field> ignoreMap;
|
||||
|
||||
public FieldCache(Map<Integer, Field> sortedAllFiledMap, Map<Integer, Field> indexFiledMap,
|
||||
Map<String, Field> ignoreMap) {
|
||||
this.sortedAllFiledMap = sortedAllFiledMap;
|
||||
this.indexFiledMap = indexFiledMap;
|
||||
this.ignoreMap = ignoreMap;
|
||||
}
|
||||
|
||||
public Map<Integer, Field> getSortedAllFiledMap() {
|
||||
return sortedAllFiledMap;
|
||||
}
|
||||
|
||||
public Map<Integer, Field> getIndexFiledMap() {
|
||||
return indexFiledMap;
|
||||
}
|
||||
|
||||
public Map<String, Field> getIgnoreMap() {
|
||||
return ignoreMap;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
+185
@@ -0,0 +1,185 @@
|
||||
package com.alibaba.excel.write.executor;
|
||||
|
||||
import com.alibaba.excel.context.WriteContext;
|
||||
import com.alibaba.excel.enums.HeadKindEnum;
|
||||
import com.alibaba.excel.metadata.CellData;
|
||||
import com.alibaba.excel.metadata.Head;
|
||||
import com.alibaba.excel.metadata.property.ExcelContentProperty;
|
||||
import com.alibaba.excel.util.ClassUtils;
|
||||
import com.alibaba.excel.util.CollectionUtils;
|
||||
import com.alibaba.excel.util.WorkBookUtil;
|
||||
import com.alibaba.excel.util.WriteHandlerUtils;
|
||||
import com.alibaba.excel.write.metadata.holder.AbstractWriteHolder;
|
||||
import com.alibaba.excel.write.metadata.holder.WriteHolder;
|
||||
import com.alibaba.excel.write.metadata.holder.WriteSheetHolder;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import net.sf.cglib.beans.BeanMap;
|
||||
import org.apache.poi.ss.usermodel.Cell;
|
||||
import org.apache.poi.ss.usermodel.Row;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* Add the data into excel
|
||||
*
|
||||
* @author Jiaju Zhuang
|
||||
*/
|
||||
@Slf4j
|
||||
public class ExcelWriteAddExecutor extends AbstractExcelWriteExecutor {
|
||||
|
||||
static {
|
||||
log.debug("======[ExcelWriteAddExecutor] 修复版被加载了=======");
|
||||
}
|
||||
|
||||
public ExcelWriteAddExecutor(WriteContext writeContext) {
|
||||
super(writeContext);
|
||||
}
|
||||
|
||||
public void add(List data) {
|
||||
if (CollectionUtils.isEmpty(data)) {
|
||||
data = new ArrayList();
|
||||
}
|
||||
WriteSheetHolder writeSheetHolder = writeContext.writeSheetHolder();
|
||||
int newRowIndex = writeSheetHolder.getNewRowIndexAndStartDoWrite();
|
||||
if (writeSheetHolder.isNew() && !writeSheetHolder.getExcelWriteHeadProperty().hasHead()) {
|
||||
newRowIndex += writeContext.currentWriteHolder().relativeHeadRowIndex();
|
||||
}
|
||||
// BeanMap is out of order,so use sortedAllFiledMap
|
||||
Map<Integer, Field> sortedAllFiledMap = new TreeMap<Integer, Field>();
|
||||
int relativeRowIndex = 0;
|
||||
for (Object oneRowData : data) {
|
||||
int n = relativeRowIndex + newRowIndex;
|
||||
addOneRowOfDataToExcel(oneRowData, n, relativeRowIndex, sortedAllFiledMap);
|
||||
relativeRowIndex++;
|
||||
}
|
||||
}
|
||||
|
||||
private void addOneRowOfDataToExcel(Object oneRowData, int n, int relativeRowIndex,
|
||||
Map<Integer, Field> sortedAllFiledMap) {
|
||||
if (oneRowData == null) {
|
||||
return;
|
||||
}
|
||||
WriteHandlerUtils.beforeRowCreate(writeContext, n, relativeRowIndex, Boolean.FALSE);
|
||||
Row row = WorkBookUtil.createRow(writeContext.writeSheetHolder().getSheet(), n);
|
||||
WriteHandlerUtils.afterRowCreate(writeContext, row, relativeRowIndex, Boolean.FALSE);
|
||||
if (oneRowData instanceof List) {
|
||||
addBasicTypeToExcel((List) oneRowData, row, relativeRowIndex);
|
||||
}
|
||||
else {
|
||||
addJavaObjectToExcel(oneRowData, row, relativeRowIndex, sortedAllFiledMap);
|
||||
}
|
||||
WriteHandlerUtils.afterRowDispose(writeContext, row, relativeRowIndex, Boolean.FALSE);
|
||||
}
|
||||
|
||||
private void addBasicTypeToExcel(List<Object> oneRowData, Row row, int relativeRowIndex) {
|
||||
if (CollectionUtils.isEmpty(oneRowData)) {
|
||||
return;
|
||||
}
|
||||
Map<Integer, Head> headMap = writeContext.currentWriteHolder().excelWriteHeadProperty().getHeadMap();
|
||||
int dataIndex = 0;
|
||||
int cellIndex = 0;
|
||||
for (Map.Entry<Integer, Head> entry : headMap.entrySet()) {
|
||||
if (dataIndex >= oneRowData.size()) {
|
||||
return;
|
||||
}
|
||||
cellIndex = entry.getKey();
|
||||
Head head = entry.getValue();
|
||||
doAddBasicTypeToExcel(oneRowData, head, row, relativeRowIndex, dataIndex++, cellIndex);
|
||||
}
|
||||
// Finish
|
||||
if (dataIndex >= oneRowData.size()) {
|
||||
return;
|
||||
}
|
||||
if (cellIndex != 0) {
|
||||
cellIndex++;
|
||||
}
|
||||
int size = oneRowData.size() - dataIndex;
|
||||
for (int i = 0; i < size; i++) {
|
||||
doAddBasicTypeToExcel(oneRowData, null, row, relativeRowIndex, dataIndex++, cellIndex++);
|
||||
}
|
||||
}
|
||||
|
||||
private void doAddBasicTypeToExcel(List<Object> oneRowData, Head head, Row row, int relativeRowIndex, int dataIndex,
|
||||
int cellIndex) {
|
||||
WriteHandlerUtils.beforeCellCreate(writeContext, row, head, cellIndex, relativeRowIndex, Boolean.FALSE);
|
||||
Cell cell = WorkBookUtil.createCell(row, cellIndex);
|
||||
WriteHandlerUtils.afterCellCreate(writeContext, cell, head, relativeRowIndex, Boolean.FALSE);
|
||||
Object value = oneRowData.get(dataIndex);
|
||||
CellData cellData = converterAndSet(writeContext.currentWriteHolder(), value == null ? null : value.getClass(),
|
||||
cell, value, null, head, relativeRowIndex);
|
||||
WriteHandlerUtils.afterCellDispose(writeContext, cellData, cell, head, relativeRowIndex, Boolean.FALSE);
|
||||
}
|
||||
|
||||
private void addJavaObjectToExcel(Object oneRowData, Row row, int relativeRowIndex,
|
||||
Map<Integer, Field> sortedAllFiledMap) {
|
||||
WriteHolder currentWriteHolder = writeContext.currentWriteHolder();
|
||||
BeanMap beanMap = BeanMap.create(oneRowData);
|
||||
Set<String> beanMapHandledSet = new HashSet<String>();
|
||||
int cellIndex = 0;
|
||||
// If it's a class it needs to be cast by type
|
||||
if (HeadKindEnum.CLASS.equals(writeContext.currentWriteHolder().excelWriteHeadProperty().getHeadKind())) {
|
||||
Map<Integer, Head> headMap = writeContext.currentWriteHolder().excelWriteHeadProperty().getHeadMap();
|
||||
Map<Integer, ExcelContentProperty> contentPropertyMap = writeContext.currentWriteHolder()
|
||||
.excelWriteHeadProperty().getContentPropertyMap();
|
||||
for (Map.Entry<Integer, ExcelContentProperty> entry : contentPropertyMap.entrySet()) {
|
||||
cellIndex = entry.getKey();
|
||||
ExcelContentProperty excelContentProperty = entry.getValue();
|
||||
String name = excelContentProperty.getField().getName();
|
||||
if (!beanMap.containsKey(name)) {
|
||||
continue;
|
||||
}
|
||||
Head head = headMap.get(cellIndex);
|
||||
WriteHandlerUtils.beforeCellCreate(writeContext, row, head, cellIndex, relativeRowIndex, Boolean.FALSE);
|
||||
Cell cell = WorkBookUtil.createCell(row, cellIndex);
|
||||
WriteHandlerUtils.afterCellCreate(writeContext, cell, head, relativeRowIndex, Boolean.FALSE);
|
||||
Object value = beanMap.get(name);
|
||||
CellData cellData = converterAndSet(currentWriteHolder, excelContentProperty.getField().getType(), cell,
|
||||
value, excelContentProperty, head, relativeRowIndex);
|
||||
WriteHandlerUtils.afterCellDispose(writeContext, cellData, cell, head, relativeRowIndex, Boolean.FALSE);
|
||||
beanMapHandledSet.add(name);
|
||||
}
|
||||
}
|
||||
// Finish
|
||||
if (beanMapHandledSet.size() == beanMap.size()) {
|
||||
return;
|
||||
}
|
||||
Map<String, Field> ignoreMap = writeContext.currentWriteHolder().excelWriteHeadProperty().getIgnoreMap();
|
||||
initSortedAllFiledMapFieldList(oneRowData.getClass(), sortedAllFiledMap);
|
||||
for (Map.Entry<Integer, Field> entry : sortedAllFiledMap.entrySet()) {
|
||||
cellIndex = entry.getKey();
|
||||
Field field = entry.getValue();
|
||||
String filedName = field.getName();
|
||||
boolean uselessData = !beanMap.containsKey(filedName) || beanMapHandledSet.contains(filedName)
|
||||
|| ignoreMap.containsKey(filedName);
|
||||
if (uselessData) {
|
||||
continue;
|
||||
}
|
||||
Object value = beanMap.get(filedName);
|
||||
WriteHandlerUtils.beforeCellCreate(writeContext, row, null, cellIndex, relativeRowIndex, Boolean.FALSE);
|
||||
Cell cell = WorkBookUtil.createCell(row, cellIndex);
|
||||
WriteHandlerUtils.afterCellCreate(writeContext, cell, null, relativeRowIndex, Boolean.FALSE);
|
||||
CellData cellData = converterAndSet(currentWriteHolder, value == null ? null : value.getClass(), cell,
|
||||
value, null, null, relativeRowIndex);
|
||||
WriteHandlerUtils.afterCellDispose(writeContext, cellData, cell, null, relativeRowIndex, Boolean.FALSE);
|
||||
}
|
||||
}
|
||||
|
||||
private void initSortedAllFiledMapFieldList(Class clazz, Map<Integer, Field> sortedAllFiledMap) {
|
||||
if (!sortedAllFiledMap.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 获取当前的使用的 holder
|
||||
WriteHolder holder = writeContext.currentWriteHolder();
|
||||
boolean needIgnore = (holder instanceof AbstractWriteHolder)
|
||||
&& (!CollectionUtils.isEmpty(((AbstractWriteHolder) holder).getExcludeColumnFiledNames())
|
||||
|| !CollectionUtils.isEmpty(((AbstractWriteHolder) holder).getExcludeColumnIndexes())
|
||||
|| !CollectionUtils.isEmpty(((AbstractWriteHolder) holder).getIncludeColumnFiledNames())
|
||||
|| !CollectionUtils.isEmpty(((AbstractWriteHolder) holder).getIncludeColumnIndexes()));
|
||||
|
||||
ClassUtils.declaredFields(clazz, sortedAllFiledMap,
|
||||
writeContext.writeWorkbookHolder().getWriteWorkbook().getConvertAllFiled(), needIgnore, holder);
|
||||
}
|
||||
|
||||
}
|
||||
+85
@@ -0,0 +1,85 @@
|
||||
package com.pig4cloud.pigx.common.excel;
|
||||
|
||||
import com.alibaba.excel.converters.Converter;
|
||||
import com.pig4cloud.pigx.common.excel.aop.ResponseExcelReturnValueHandler;
|
||||
import com.pig4cloud.pigx.common.excel.config.ExcelConfigProperties;
|
||||
import com.pig4cloud.pigx.common.excel.enhance.DefaultWriterBuilderEnhancer;
|
||||
import com.pig4cloud.pigx.common.excel.enhance.WriterBuilderEnhancer;
|
||||
import com.pig4cloud.pigx.common.excel.handler.ManySheetWriteHandler;
|
||||
import com.pig4cloud.pigx.common.excel.handler.SheetWriteHandler;
|
||||
import com.pig4cloud.pigx.common.excel.handler.SingleSheetWriteHandler;
|
||||
import com.pig4cloud.pigx.common.excel.head.I18nHeaderCellWriteHandler;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.context.MessageSource;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author Hccake 2020/10/28
|
||||
* @version 1.0
|
||||
*/
|
||||
@RequiredArgsConstructor
|
||||
@Configuration
|
||||
public class ExcelHandlerConfiguration {
|
||||
|
||||
private final ExcelConfigProperties configProperties;
|
||||
|
||||
private final ObjectProvider<List<Converter<?>>> converterProvider;
|
||||
|
||||
/**
|
||||
* ExcelBuild增强
|
||||
* @return DefaultWriterBuilderEnhancer 默认什么也不做的增强器
|
||||
*/
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public WriterBuilderEnhancer writerBuilderEnhancer() {
|
||||
return new DefaultWriterBuilderEnhancer();
|
||||
}
|
||||
|
||||
/**
|
||||
* 单sheet 写入处理器
|
||||
*/
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public SingleSheetWriteHandler singleSheetWriteHandler() {
|
||||
return new SingleSheetWriteHandler(configProperties, converterProvider, writerBuilderEnhancer());
|
||||
}
|
||||
|
||||
/**
|
||||
* 多sheet 写入处理器
|
||||
*/
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public ManySheetWriteHandler manySheetWriteHandler() {
|
||||
return new ManySheetWriteHandler(configProperties, converterProvider, writerBuilderEnhancer());
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回Excel文件的 response 处理器
|
||||
* @param sheetWriteHandlerList 页签写入处理器集合
|
||||
* @return ResponseExcelReturnValueHandler
|
||||
*/
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public ResponseExcelReturnValueHandler responseExcelReturnValueHandler(
|
||||
List<SheetWriteHandler> sheetWriteHandlerList) {
|
||||
return new ResponseExcelReturnValueHandler(sheetWriteHandlerList);
|
||||
}
|
||||
|
||||
/**
|
||||
* excel 头的国际化处理器
|
||||
* @param messageSource 国际化源
|
||||
*/
|
||||
@Bean
|
||||
@ConditionalOnBean(MessageSource.class)
|
||||
@ConditionalOnMissingBean
|
||||
public I18nHeaderCellWriteHandler i18nHeaderCellWriteHandler(MessageSource messageSource) {
|
||||
return new I18nHeaderCellWriteHandler(messageSource);
|
||||
}
|
||||
|
||||
}
|
||||
+87
@@ -0,0 +1,87 @@
|
||||
package com.pig4cloud.pigx.common.excel;
|
||||
|
||||
import com.pig4cloud.pigx.common.excel.aop.DynamicNameAspect;
|
||||
import com.pig4cloud.pigx.common.excel.aop.RequestExcelArgumentResolver;
|
||||
import com.pig4cloud.pigx.common.excel.aop.ResponseExcelReturnValueHandler;
|
||||
import com.pig4cloud.pigx.common.excel.config.ExcelConfigProperties;
|
||||
import com.pig4cloud.pigx.common.excel.processor.NameProcessor;
|
||||
import com.pig4cloud.pigx.common.excel.processor.NameSpelExpressionProcessor;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
|
||||
import org.springframework.web.method.support.HandlerMethodReturnValueHandler;
|
||||
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author lengleng
|
||||
* @date 2020/3/29
|
||||
* <p>
|
||||
* 配置初始化
|
||||
*/
|
||||
@Import(ExcelHandlerConfiguration.class)
|
||||
@RequiredArgsConstructor
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@EnableConfigurationProperties(ExcelConfigProperties.class)
|
||||
public class ResponseExcelAutoConfiguration {
|
||||
|
||||
private final RequestMappingHandlerAdapter requestMappingHandlerAdapter;
|
||||
|
||||
private final ResponseExcelReturnValueHandler responseExcelReturnValueHandler;
|
||||
|
||||
/**
|
||||
* SPEL 解析处理器
|
||||
* @return NameProcessor excel名称解析器
|
||||
*/
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public NameProcessor nameProcessor() {
|
||||
return new NameSpelExpressionProcessor();
|
||||
}
|
||||
|
||||
/**
|
||||
* Excel名称解析处理切面
|
||||
* @param nameProcessor SPEL 解析处理器
|
||||
* @return DynamicNameAspect
|
||||
*/
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public DynamicNameAspect dynamicNameAspect(NameProcessor nameProcessor) {
|
||||
return new DynamicNameAspect(nameProcessor);
|
||||
}
|
||||
|
||||
/**
|
||||
* 追加 Excel返回值处理器 到 springmvc 中
|
||||
*/
|
||||
@PostConstruct
|
||||
public void setReturnValueHandlers() {
|
||||
List<HandlerMethodReturnValueHandler> returnValueHandlers = requestMappingHandlerAdapter
|
||||
.getReturnValueHandlers();
|
||||
|
||||
List<HandlerMethodReturnValueHandler> newHandlers = new ArrayList<>();
|
||||
newHandlers.add(responseExcelReturnValueHandler);
|
||||
assert returnValueHandlers != null;
|
||||
newHandlers.addAll(returnValueHandlers);
|
||||
requestMappingHandlerAdapter.setReturnValueHandlers(newHandlers);
|
||||
}
|
||||
|
||||
/**
|
||||
* 追加 Excel 请求处理器 到 springmvc 中
|
||||
*/
|
||||
@PostConstruct
|
||||
public void setRequestExcelArgumentResolver() {
|
||||
List<HandlerMethodArgumentResolver> argumentResolvers = requestMappingHandlerAdapter.getArgumentResolvers();
|
||||
List<HandlerMethodArgumentResolver> resolverList = new ArrayList<>();
|
||||
resolverList.add(new RequestExcelArgumentResolver());
|
||||
resolverList.addAll(argumentResolvers);
|
||||
requestMappingHandlerAdapter.setArgumentResolvers(resolverList);
|
||||
}
|
||||
|
||||
}
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
package com.pig4cloud.pigx.common.excel.annotation;
|
||||
|
||||
import com.pig4cloud.pigx.common.excel.handler.DefaultAnalysisEventListener;
|
||||
import com.pig4cloud.pigx.common.excel.handler.ListAnalysisEventListener;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
/**
|
||||
* 导入excel
|
||||
*
|
||||
* @author lengleng
|
||||
* @author L.cm
|
||||
* @date 2021/4/16
|
||||
*/
|
||||
@Documented
|
||||
@Target({ ElementType.PARAMETER })
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface RequestExcel {
|
||||
|
||||
/**
|
||||
* 前端上传字段名称 file
|
||||
*/
|
||||
String fileName() default "file";
|
||||
|
||||
/**
|
||||
* 读取的监听器类
|
||||
* @return readListener
|
||||
*/
|
||||
Class<? extends ListAnalysisEventListener<?>> readListener() default DefaultAnalysisEventListener.class;
|
||||
|
||||
/**
|
||||
* 是否跳过空行
|
||||
* @return 默认跳过
|
||||
*/
|
||||
boolean ignoreEmptyRow() default false;
|
||||
|
||||
}
|
||||
+92
@@ -0,0 +1,92 @@
|
||||
package com.pig4cloud.pigx.common.excel.annotation;
|
||||
|
||||
import com.alibaba.excel.converters.Converter;
|
||||
import com.alibaba.excel.support.ExcelTypeEnum;
|
||||
import com.alibaba.excel.write.handler.WriteHandler;
|
||||
import com.pig4cloud.pigx.common.excel.head.HeadGenerator;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
/**
|
||||
* `@ResponseExcel 注解`
|
||||
*
|
||||
* @author lengleng
|
||||
*/
|
||||
@Documented
|
||||
@Target(ElementType.METHOD)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface ResponseExcel {
|
||||
|
||||
/**
|
||||
* 文件名称
|
||||
* @return string
|
||||
*/
|
||||
String name() default "";
|
||||
|
||||
/**
|
||||
* 文件类型 (xlsx xls)
|
||||
* @return string
|
||||
*/
|
||||
ExcelTypeEnum suffix() default ExcelTypeEnum.XLSX;
|
||||
|
||||
/**
|
||||
* 文件密码
|
||||
* @return password
|
||||
*/
|
||||
String password() default "";
|
||||
|
||||
/**
|
||||
* sheet 名称,支持多个
|
||||
* @return String[]
|
||||
*/
|
||||
Sheet[] sheets() default @Sheet(sheetName = "sheet1");
|
||||
|
||||
/**
|
||||
* 内存操作
|
||||
* @return
|
||||
*/
|
||||
boolean inMemory() default false;
|
||||
|
||||
/**
|
||||
* excel 模板
|
||||
* @return String
|
||||
*/
|
||||
String template() default "";
|
||||
|
||||
/**
|
||||
* + 包含字段
|
||||
* @return String[]
|
||||
*/
|
||||
String[] include() default {};
|
||||
|
||||
/**
|
||||
* 排除字段
|
||||
* @return String[]
|
||||
*/
|
||||
String[] exclude() default {};
|
||||
|
||||
/**
|
||||
* 拦截器,自定义样式等处理器
|
||||
* @return WriteHandler[]
|
||||
*/
|
||||
Class<? extends WriteHandler>[] writeHandler() default {};
|
||||
|
||||
/**
|
||||
* 转换器
|
||||
* @return Converter[]
|
||||
*/
|
||||
Class<? extends Converter>[] converter() default {};
|
||||
|
||||
/**
|
||||
* 自定义Excel头生成器
|
||||
* @return HeadGenerator
|
||||
*/
|
||||
Class<? extends HeadGenerator> headGenerator() default HeadGenerator.class;
|
||||
|
||||
/**
|
||||
* excel 头信息国际化
|
||||
* @return boolean
|
||||
*/
|
||||
boolean i18nHeader() default false;
|
||||
|
||||
}
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
package com.pig4cloud.pigx.common.excel.annotation;
|
||||
|
||||
import com.pig4cloud.pigx.common.excel.head.HeadGenerator;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
/**
|
||||
* @author Yakir
|
||||
* @Topic Sheet
|
||||
* @Description
|
||||
* @date 2021/4/29 15:03
|
||||
* @Version 1.0
|
||||
*/
|
||||
@Target(ElementType.METHOD)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
public @interface Sheet {
|
||||
|
||||
int sheetNo() default -1;
|
||||
|
||||
/**
|
||||
* sheet name
|
||||
*/
|
||||
String sheetName();
|
||||
|
||||
/**
|
||||
* 包含字段
|
||||
*/
|
||||
String[] includes() default {};
|
||||
|
||||
/**
|
||||
* 排除字段
|
||||
*/
|
||||
String[] excludes() default {};
|
||||
|
||||
/**
|
||||
* 头生成器
|
||||
*/
|
||||
Class<? extends HeadGenerator> headGenerateClass() default HeadGenerator.class;
|
||||
|
||||
}
|
||||
+46
@@ -0,0 +1,46 @@
|
||||
package com.pig4cloud.pigx.common.excel.aop;
|
||||
|
||||
import com.pig4cloud.pigx.common.excel.annotation.ResponseExcel;
|
||||
import com.pig4cloud.pigx.common.excel.processor.NameProcessor;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.aspectj.lang.JoinPoint;
|
||||
import org.aspectj.lang.annotation.Aspect;
|
||||
import org.aspectj.lang.annotation.Before;
|
||||
import org.aspectj.lang.reflect.MethodSignature;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.context.request.RequestAttributes;
|
||||
import org.springframework.web.context.request.RequestContextHolder;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* @author lengleng
|
||||
* @date 2020/3/29
|
||||
*/
|
||||
@Aspect
|
||||
@RequiredArgsConstructor
|
||||
public class DynamicNameAspect {
|
||||
|
||||
public static final String EXCEL_NAME_KEY = "__EXCEL_NAME_KEY__";
|
||||
|
||||
private final NameProcessor processor;
|
||||
|
||||
@Before("@annotation(excel)")
|
||||
public void around(JoinPoint point, ResponseExcel excel) {
|
||||
MethodSignature ms = (MethodSignature) point.getSignature();
|
||||
|
||||
String name = excel.name();
|
||||
// 当配置的 excel 名称为空时,取当前时间
|
||||
if (!StringUtils.hasText(name)) {
|
||||
name = LocalDateTime.now().toString();
|
||||
}
|
||||
else {
|
||||
name = processor.doDetermineName(point.getArgs(), ms.getMethod(), excel.name());
|
||||
}
|
||||
|
||||
RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
|
||||
Objects.requireNonNull(requestAttributes).setAttribute(EXCEL_NAME_KEY, name, RequestAttributes.SCOPE_REQUEST);
|
||||
}
|
||||
|
||||
}
|
||||
+87
@@ -0,0 +1,87 @@
|
||||
package com.pig4cloud.pigx.common.excel.aop;
|
||||
|
||||
import com.alibaba.excel.EasyExcel;
|
||||
import com.pig4cloud.pigx.common.excel.annotation.RequestExcel;
|
||||
import com.pig4cloud.pigx.common.excel.converters.LocalDateStringConverter;
|
||||
import com.pig4cloud.pigx.common.excel.converters.LocalDateTimeStringConverter;
|
||||
import com.pig4cloud.pigx.common.excel.handler.ListAnalysisEventListener;
|
||||
import lombok.SneakyThrows;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.core.ResolvableType;
|
||||
import org.springframework.ui.ModelMap;
|
||||
import org.springframework.validation.BindingResult;
|
||||
import org.springframework.web.bind.WebDataBinder;
|
||||
import org.springframework.web.bind.support.WebDataBinderFactory;
|
||||
import org.springframework.web.context.request.NativeWebRequest;
|
||||
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
|
||||
import org.springframework.web.method.support.ModelAndViewContainer;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
import org.springframework.web.multipart.MultipartRequest;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import java.io.InputStream;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 上传excel 解析注解
|
||||
*
|
||||
* @author lengleng
|
||||
* @author L.cm
|
||||
* @date 2021/4/16
|
||||
*/
|
||||
@Slf4j
|
||||
public class RequestExcelArgumentResolver implements HandlerMethodArgumentResolver {
|
||||
|
||||
@Override
|
||||
public boolean supportsParameter(MethodParameter parameter) {
|
||||
return parameter.hasParameterAnnotation(RequestExcel.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
@SneakyThrows
|
||||
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer modelAndViewContainer,
|
||||
NativeWebRequest webRequest, WebDataBinderFactory webDataBinderFactory) {
|
||||
Class<?> parameterType = parameter.getParameterType();
|
||||
if (!parameterType.isAssignableFrom(List.class)) {
|
||||
throw new IllegalArgumentException(
|
||||
"Excel upload request resolver error, @RequestExcel parameter is not List " + parameterType);
|
||||
}
|
||||
|
||||
// 处理自定义 readListener
|
||||
RequestExcel requestExcel = parameter.getParameterAnnotation(RequestExcel.class);
|
||||
assert requestExcel != null;
|
||||
Class<? extends ListAnalysisEventListener<?>> readListenerClass = requestExcel.readListener();
|
||||
ListAnalysisEventListener<?> readListener = BeanUtils.instantiateClass(readListenerClass);
|
||||
|
||||
// 获取请求文件流
|
||||
HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);
|
||||
assert request != null;
|
||||
InputStream inputStream;
|
||||
if (request instanceof MultipartRequest) {
|
||||
MultipartFile file = ((MultipartRequest) request).getFile(requestExcel.fileName());
|
||||
assert file != null;
|
||||
inputStream = file.getInputStream();
|
||||
}
|
||||
else {
|
||||
inputStream = request.getInputStream();
|
||||
}
|
||||
|
||||
// 获取目标类型
|
||||
Class<?> excelModelClass = ResolvableType.forMethodParameter(parameter).getGeneric(0).resolve();
|
||||
|
||||
// 这里需要指定读用哪个 class 去读,然后读取第一个 sheet 文件流会自动关闭
|
||||
EasyExcel.read(inputStream, excelModelClass, readListener).registerConverter(LocalDateStringConverter.INSTANCE)
|
||||
.registerConverter(LocalDateTimeStringConverter.INSTANCE).ignoreEmptyRow(requestExcel.ignoreEmptyRow())
|
||||
.sheet().doRead();
|
||||
|
||||
// 校验失败的数据处理 交给 BindResult
|
||||
WebDataBinder dataBinder = webDataBinderFactory.createBinder(webRequest, readListener.getErrors(), "excel");
|
||||
ModelMap model = modelAndViewContainer.getModel();
|
||||
model.put(BindingResult.MODEL_KEY_PREFIX + "excel", dataBinder.getBindingResult());
|
||||
|
||||
return readListener.getList();
|
||||
}
|
||||
|
||||
}
|
||||
+59
@@ -0,0 +1,59 @@
|
||||
package com.pig4cloud.pigx.common.excel.aop;
|
||||
|
||||
import com.pig4cloud.pigx.common.excel.annotation.ResponseExcel;
|
||||
import com.pig4cloud.pigx.common.excel.handler.SheetWriteHandler;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.web.context.request.NativeWebRequest;
|
||||
import org.springframework.web.method.support.HandlerMethodReturnValueHandler;
|
||||
import org.springframework.web.method.support.ModelAndViewContainer;
|
||||
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 处理@ResponseExcel 返回值
|
||||
*
|
||||
* @author lengleng
|
||||
*/
|
||||
@Slf4j
|
||||
@RequiredArgsConstructor
|
||||
public class ResponseExcelReturnValueHandler implements HandlerMethodReturnValueHandler {
|
||||
|
||||
private final List<SheetWriteHandler> sheetWriteHandlerList;
|
||||
|
||||
/**
|
||||
* 只处理@ResponseExcel 声明的方法
|
||||
* @param parameter 方法签名
|
||||
* @return 是否处理
|
||||
*/
|
||||
@Override
|
||||
public boolean supportsReturnType(MethodParameter parameter) {
|
||||
return parameter.getMethodAnnotation(ResponseExcel.class) != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理逻辑
|
||||
* @param o 返回参数
|
||||
* @param parameter 方法签名
|
||||
* @param mavContainer 上下文容器
|
||||
* @param nativeWebRequest 上下文
|
||||
* @throws Exception 处理异常
|
||||
*/
|
||||
@Override
|
||||
public void handleReturnValue(Object o, MethodParameter parameter, ModelAndViewContainer mavContainer,
|
||||
NativeWebRequest nativeWebRequest) throws Exception {
|
||||
/* check */
|
||||
HttpServletResponse response = nativeWebRequest.getNativeResponse(HttpServletResponse.class);
|
||||
Assert.state(response != null, "No HttpServletResponse");
|
||||
ResponseExcel responseExcel = parameter.getMethodAnnotation(ResponseExcel.class);
|
||||
Assert.state(responseExcel != null, "No @ResponseExcel");
|
||||
mavContainer.setRequestHandled(true);
|
||||
|
||||
sheetWriteHandlerList.stream().filter(handler -> handler.support(o)).findFirst()
|
||||
.ifPresent(handler -> handler.export(o, response, responseExcel));
|
||||
}
|
||||
|
||||
}
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
package com.pig4cloud.pigx.common.excel.config;
|
||||
|
||||
import lombok.Data;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
|
||||
/**
|
||||
* @author lengleng
|
||||
* @date 2020/3/29
|
||||
*/
|
||||
@Data
|
||||
@ConfigurationProperties(prefix = ExcelConfigProperties.PREFIX)
|
||||
public class ExcelConfigProperties {
|
||||
|
||||
static final String PREFIX = "excel";
|
||||
|
||||
/**
|
||||
* 模板路径
|
||||
*/
|
||||
private String templatePath = "excel";
|
||||
|
||||
}
|
||||
+61
@@ -0,0 +1,61 @@
|
||||
package com.pig4cloud.pigx.common.excel.converters;
|
||||
|
||||
import com.alibaba.excel.converters.Converter;
|
||||
import com.alibaba.excel.enums.CellDataTypeEnum;
|
||||
import com.alibaba.excel.metadata.CellData;
|
||||
import com.alibaba.excel.metadata.GlobalConfiguration;
|
||||
import com.alibaba.excel.metadata.property.ExcelContentProperty;
|
||||
|
||||
import java.text.ParseException;
|
||||
import java.time.LocalDate;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
|
||||
/**
|
||||
* LocalDate and string converter
|
||||
*
|
||||
* @author L.cm
|
||||
*/
|
||||
public enum LocalDateStringConverter implements Converter<LocalDate> {
|
||||
|
||||
/**
|
||||
* 实例
|
||||
*/
|
||||
INSTANCE;
|
||||
|
||||
@Override
|
||||
public Class supportJavaTypeKey() {
|
||||
return LocalDate.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CellDataTypeEnum supportExcelTypeKey() {
|
||||
return CellDataTypeEnum.STRING;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LocalDate convertToJavaData(CellData cellData, ExcelContentProperty contentProperty,
|
||||
GlobalConfiguration globalConfiguration) throws ParseException {
|
||||
if (contentProperty == null || contentProperty.getDateTimeFormatProperty() == null) {
|
||||
return LocalDate.parse(cellData.getStringValue());
|
||||
}
|
||||
else {
|
||||
DateTimeFormatter formatter = DateTimeFormatter
|
||||
.ofPattern(contentProperty.getDateTimeFormatProperty().getFormat());
|
||||
return LocalDate.parse(cellData.getStringValue(), formatter);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public CellData<String> convertToExcelData(LocalDate value, ExcelContentProperty contentProperty,
|
||||
GlobalConfiguration globalConfiguration) {
|
||||
DateTimeFormatter formatter;
|
||||
if (contentProperty == null || contentProperty.getDateTimeFormatProperty() == null) {
|
||||
formatter = DateTimeFormatter.ISO_LOCAL_DATE;
|
||||
}
|
||||
else {
|
||||
formatter = DateTimeFormatter.ofPattern(contentProperty.getDateTimeFormatProperty().getFormat());
|
||||
}
|
||||
return new CellData<>(value.format(formatter));
|
||||
}
|
||||
|
||||
}
|
||||
+93
@@ -0,0 +1,93 @@
|
||||
package com.pig4cloud.pigx.common.excel.converters;
|
||||
|
||||
import com.alibaba.excel.converters.Converter;
|
||||
import com.alibaba.excel.enums.CellDataTypeEnum;
|
||||
import com.alibaba.excel.metadata.CellData;
|
||||
import com.alibaba.excel.metadata.GlobalConfiguration;
|
||||
import com.alibaba.excel.metadata.property.ExcelContentProperty;
|
||||
import com.alibaba.excel.util.DateUtils;
|
||||
|
||||
import java.text.ParseException;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
|
||||
/**
|
||||
* LocalDateTime and string converter
|
||||
*
|
||||
* @author L.cm
|
||||
*/
|
||||
public enum LocalDateTimeStringConverter implements Converter<LocalDateTime> {
|
||||
|
||||
/**
|
||||
* 实例
|
||||
*/
|
||||
INSTANCE;
|
||||
|
||||
private static final String MINUS = "-";
|
||||
|
||||
@Override
|
||||
public Class supportJavaTypeKey() {
|
||||
return LocalDateTime.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CellDataTypeEnum supportExcelTypeKey() {
|
||||
return CellDataTypeEnum.STRING;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LocalDateTime convertToJavaData(CellData cellData, ExcelContentProperty contentProperty,
|
||||
GlobalConfiguration globalConfiguration) throws ParseException {
|
||||
String stringValue = cellData.getStringValue();
|
||||
String pattern;
|
||||
if (contentProperty == null || contentProperty.getDateTimeFormatProperty() == null) {
|
||||
pattern = switchDateFormat(stringValue);
|
||||
}
|
||||
else {
|
||||
pattern = contentProperty.getDateTimeFormatProperty().getFormat();
|
||||
}
|
||||
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(pattern);
|
||||
return LocalDateTime.parse(cellData.getStringValue(), formatter);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CellData<String> convertToExcelData(LocalDateTime value, ExcelContentProperty contentProperty,
|
||||
GlobalConfiguration globalConfiguration) {
|
||||
String pattern;
|
||||
if (contentProperty == null || contentProperty.getDateTimeFormatProperty() == null) {
|
||||
pattern = DateUtils.DATE_FORMAT_19;
|
||||
}
|
||||
else {
|
||||
pattern = contentProperty.getDateTimeFormatProperty().getFormat();
|
||||
}
|
||||
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(pattern);
|
||||
return new CellData<>(value.format(formatter));
|
||||
}
|
||||
|
||||
/**
|
||||
* switch date format
|
||||
* @param dateString dateString
|
||||
* @return pattern
|
||||
*/
|
||||
private static String switchDateFormat(String dateString) {
|
||||
int length = dateString.length();
|
||||
switch (length) {
|
||||
case 19:
|
||||
if (dateString.contains(MINUS)) {
|
||||
return DateUtils.DATE_FORMAT_19;
|
||||
}
|
||||
else {
|
||||
return DateUtils.DATE_FORMAT_19_FORWARD_SLASH;
|
||||
}
|
||||
case 17:
|
||||
return DateUtils.DATE_FORMAT_17;
|
||||
case 14:
|
||||
return DateUtils.DATE_FORMAT_14;
|
||||
case 10:
|
||||
return DateUtils.DATE_FORMAT_10;
|
||||
default:
|
||||
throw new IllegalArgumentException("can not find date format for:" + dateString);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
package com.pig4cloud.pigx.common.excel.enhance;
|
||||
|
||||
import com.alibaba.excel.write.builder.ExcelWriterBuilder;
|
||||
import com.alibaba.excel.write.builder.ExcelWriterSheetBuilder;
|
||||
import com.pig4cloud.pigx.common.excel.annotation.ResponseExcel;
|
||||
import com.pig4cloud.pigx.common.excel.head.HeadGenerator;
|
||||
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
/**
|
||||
* @author Hccake 2020/12/18
|
||||
* @version 1.0
|
||||
*/
|
||||
public class DefaultWriterBuilderEnhancer implements WriterBuilderEnhancer {
|
||||
|
||||
/**
|
||||
* ExcelWriterBuilder 增强
|
||||
* @param writerBuilder ExcelWriterBuilder
|
||||
* @param response HttpServletResponse
|
||||
* @param responseExcel ResponseExcel
|
||||
* @param templatePath 模板地址
|
||||
* @return ExcelWriterBuilder
|
||||
*/
|
||||
@Override
|
||||
public ExcelWriterBuilder enhanceExcel(ExcelWriterBuilder writerBuilder, HttpServletResponse response,
|
||||
ResponseExcel responseExcel, String templatePath) {
|
||||
// doNothing
|
||||
return writerBuilder;
|
||||
}
|
||||
|
||||
/**
|
||||
* ExcelWriterSheetBuilder 增强
|
||||
* @param writerSheetBuilder ExcelWriterSheetBuilder
|
||||
* @param sheetNo sheet角标
|
||||
* @param sheetName sheet名,有模板时为空
|
||||
* @param dataClass 当前写入的数据所属类
|
||||
* @param template 模板文件
|
||||
* @param headEnhancerClass 当前指定的自定义头处理器
|
||||
* @return ExcelWriterSheetBuilder
|
||||
*/
|
||||
@Override
|
||||
public ExcelWriterSheetBuilder enhanceSheet(ExcelWriterSheetBuilder writerSheetBuilder, Integer sheetNo,
|
||||
String sheetName, Class<?> dataClass, String template, Class<? extends HeadGenerator> headEnhancerClass) {
|
||||
// doNothing
|
||||
return writerSheetBuilder;
|
||||
}
|
||||
|
||||
}
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
package com.pig4cloud.pigx.common.excel.enhance;
|
||||
|
||||
import com.alibaba.excel.write.builder.ExcelWriterBuilder;
|
||||
import com.alibaba.excel.write.builder.ExcelWriterSheetBuilder;
|
||||
import com.pig4cloud.pigx.common.excel.annotation.ResponseExcel;
|
||||
import com.pig4cloud.pigx.common.excel.head.HeadGenerator;
|
||||
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
/**
|
||||
* ExcelWriterBuilder 增强
|
||||
*
|
||||
* @author Hccake 2020/12/18
|
||||
* @version 1.0
|
||||
*/
|
||||
public interface WriterBuilderEnhancer {
|
||||
|
||||
/**
|
||||
* ExcelWriterBuilder 增强
|
||||
* @param writerBuilder ExcelWriterBuilder
|
||||
* @param response HttpServletResponse
|
||||
* @param responseExcel ResponseExcel
|
||||
* @param templatePath 模板地址
|
||||
* @return ExcelWriterBuilder
|
||||
*/
|
||||
ExcelWriterBuilder enhanceExcel(ExcelWriterBuilder writerBuilder, HttpServletResponse response,
|
||||
ResponseExcel responseExcel, String templatePath);
|
||||
|
||||
/**
|
||||
* ExcelWriterSheetBuilder 增强
|
||||
* @param writerSheetBuilder ExcelWriterSheetBuilder
|
||||
* @param sheetNo sheet角标
|
||||
* @param sheetName sheet名,有模板时为空
|
||||
* @param dataClass 当前写入的数据所属类
|
||||
* @param template 模板文件
|
||||
* @param headEnhancerClass 当前指定的自定义头处理器
|
||||
* @return ExcelWriterSheetBuilder
|
||||
*/
|
||||
ExcelWriterSheetBuilder enhanceSheet(ExcelWriterSheetBuilder writerSheetBuilder, Integer sheetNo, String sheetName,
|
||||
Class<?> dataClass, String template, Class<? extends HeadGenerator> headEnhancerClass);
|
||||
|
||||
}
|
||||
+231
@@ -0,0 +1,231 @@
|
||||
package com.pig4cloud.pigx.common.excel.handler;
|
||||
|
||||
import com.alibaba.excel.EasyExcel;
|
||||
import com.alibaba.excel.ExcelWriter;
|
||||
import com.alibaba.excel.converters.Converter;
|
||||
import com.alibaba.excel.write.builder.ExcelWriterBuilder;
|
||||
import com.alibaba.excel.write.builder.ExcelWriterSheetBuilder;
|
||||
import com.alibaba.excel.write.handler.WriteHandler;
|
||||
import com.alibaba.excel.write.metadata.WriteSheet;
|
||||
import com.pig4cloud.pigx.common.excel.annotation.ResponseExcel;
|
||||
import com.pig4cloud.pigx.common.excel.annotation.Sheet;
|
||||
import com.pig4cloud.pigx.common.excel.aop.DynamicNameAspect;
|
||||
import com.pig4cloud.pigx.common.excel.config.ExcelConfigProperties;
|
||||
import com.pig4cloud.pigx.common.excel.converters.LocalDateStringConverter;
|
||||
import com.pig4cloud.pigx.common.excel.converters.LocalDateTimeStringConverter;
|
||||
import com.pig4cloud.pigx.common.excel.enhance.WriterBuilderEnhancer;
|
||||
import com.pig4cloud.pigx.common.excel.head.HeadGenerator;
|
||||
import com.pig4cloud.pigx.common.excel.head.HeadMeta;
|
||||
import com.pig4cloud.pigx.common.excel.head.I18nHeaderCellWriteHandler;
|
||||
import com.pig4cloud.pigx.common.excel.kit.ExcelException;
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.Setter;
|
||||
import lombok.SneakyThrows;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationContextAware;
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.MediaTypeFactory;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.context.request.RequestAttributes;
|
||||
import org.springframework.web.context.request.RequestContextHolder;
|
||||
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.File;
|
||||
import java.io.InputStream;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.net.URLEncoder;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* @author lengleng
|
||||
* @author L.cm
|
||||
* @date 2020/3/31
|
||||
*/
|
||||
@RequiredArgsConstructor
|
||||
public abstract class AbstractSheetWriteHandler implements SheetWriteHandler, ApplicationContextAware {
|
||||
|
||||
private final ExcelConfigProperties configProperties;
|
||||
|
||||
private final ObjectProvider<List<Converter<?>>> converterProvider;
|
||||
|
||||
private final WriterBuilderEnhancer excelWriterBuilderEnhance;
|
||||
|
||||
private ApplicationContext applicationContext;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
@Autowired(required = false)
|
||||
private I18nHeaderCellWriteHandler i18nHeaderCellWriteHandler;
|
||||
|
||||
@Override
|
||||
public void check(ResponseExcel responseExcel) {
|
||||
if (responseExcel.sheets().length == 0) {
|
||||
throw new ExcelException("@ResponseExcel sheet 配置不合法");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@SneakyThrows
|
||||
public void export(Object o, HttpServletResponse response, ResponseExcel responseExcel) {
|
||||
check(responseExcel);
|
||||
RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
|
||||
String name = (String) Objects.requireNonNull(requestAttributes).getAttribute(DynamicNameAspect.EXCEL_NAME_KEY,
|
||||
RequestAttributes.SCOPE_REQUEST);
|
||||
String fileName = String.format("%s%s", URLEncoder.encode(name, "UTF-8"), responseExcel.suffix().getValue());
|
||||
// 根据实际的文件类型找到对应的 contentType
|
||||
String contentType = MediaTypeFactory.getMediaType(fileName).map(MediaType::toString)
|
||||
.orElse("application/vnd.ms-excel");
|
||||
response.setContentType(contentType);
|
||||
response.setCharacterEncoding("utf-8");
|
||||
response.setHeader(HttpHeaders.CONTENT_DISPOSITION, "attachment;filename=" + fileName);
|
||||
write(o, response, responseExcel);
|
||||
}
|
||||
|
||||
/**
|
||||
* 通用的获取ExcelWriter方法
|
||||
* @param response HttpServletResponse
|
||||
* @param responseExcel ResponseExcel注解
|
||||
* @return ExcelWriter
|
||||
*/
|
||||
@SneakyThrows
|
||||
public ExcelWriter getExcelWriter(HttpServletResponse response, ResponseExcel responseExcel) {
|
||||
ExcelWriterBuilder writerBuilder = EasyExcel.write(response.getOutputStream())
|
||||
.registerConverter(LocalDateStringConverter.INSTANCE)
|
||||
.registerConverter(LocalDateTimeStringConverter.INSTANCE).autoCloseStream(true)
|
||||
.excelType(responseExcel.suffix()).inMemory(responseExcel.inMemory());
|
||||
|
||||
if (StringUtils.hasText(responseExcel.password())) {
|
||||
writerBuilder.password(responseExcel.password());
|
||||
}
|
||||
|
||||
if (responseExcel.include().length != 0) {
|
||||
writerBuilder.includeColumnFiledNames(Arrays.asList(responseExcel.include()));
|
||||
}
|
||||
|
||||
if (responseExcel.exclude().length != 0) {
|
||||
writerBuilder.excludeColumnFiledNames(Arrays.asList(responseExcel.exclude()));
|
||||
}
|
||||
|
||||
if (responseExcel.writeHandler().length != 0) {
|
||||
for (Class<? extends WriteHandler> clazz : responseExcel.writeHandler()) {
|
||||
writerBuilder.registerWriteHandler(BeanUtils.instantiateClass(clazz));
|
||||
}
|
||||
}
|
||||
|
||||
// 开启国际化头信息处理
|
||||
if (responseExcel.i18nHeader() && i18nHeaderCellWriteHandler != null) {
|
||||
writerBuilder.registerWriteHandler(i18nHeaderCellWriteHandler);
|
||||
}
|
||||
|
||||
// 自定义注入的转换器
|
||||
registerCustomConverter(writerBuilder);
|
||||
|
||||
if (responseExcel.converter().length != 0) {
|
||||
for (Class<? extends Converter> clazz : responseExcel.converter()) {
|
||||
writerBuilder.registerConverter(BeanUtils.instantiateClass(clazz));
|
||||
}
|
||||
}
|
||||
|
||||
String templatePath = configProperties.getTemplatePath();
|
||||
if (StringUtils.hasText(responseExcel.template())) {
|
||||
ClassPathResource classPathResource = new ClassPathResource(
|
||||
templatePath + File.separator + responseExcel.template());
|
||||
InputStream inputStream = classPathResource.getInputStream();
|
||||
writerBuilder.withTemplate(inputStream);
|
||||
}
|
||||
|
||||
writerBuilder = excelWriterBuilderEnhance.enhanceExcel(writerBuilder, response, responseExcel, templatePath);
|
||||
|
||||
return writerBuilder.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* 自定义注入转换器 如果有需要,子类自己重写
|
||||
* @param builder ExcelWriterBuilder
|
||||
*/
|
||||
public void registerCustomConverter(ExcelWriterBuilder builder) {
|
||||
converterProvider.ifAvailable(converters -> converters.forEach(builder::registerConverter));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取 WriteSheet 对象
|
||||
* @param sheet sheet annotation info
|
||||
* @param dataClass 数据类型
|
||||
* @param template 模板
|
||||
* @param bookHeadEnhancerClass 自定义头处理器
|
||||
* @return WriteSheet
|
||||
*/
|
||||
public WriteSheet sheet(Sheet sheet, Class<?> dataClass, String template,
|
||||
Class<? extends HeadGenerator> bookHeadEnhancerClass) {
|
||||
|
||||
// Sheet 编号和名称
|
||||
Integer sheetNo = sheet.sheetNo() >= 0 ? sheet.sheetNo() : null;
|
||||
String sheetName = sheet.sheetName();
|
||||
|
||||
// 是否模板写入
|
||||
ExcelWriterSheetBuilder writerSheetBuilder = StringUtils.hasText(template) ? EasyExcel.writerSheet(sheetNo)
|
||||
: EasyExcel.writerSheet(sheetNo, sheetName);
|
||||
|
||||
// 头信息增强 1. 优先使用 sheet 指定的头信息增强 2. 其次使用 @ResponseExcel 中定义的全局头信息增强
|
||||
Class<? extends HeadGenerator> headGenerateClass = null;
|
||||
if (isNotInterface(sheet.headGenerateClass())) {
|
||||
headGenerateClass = sheet.headGenerateClass();
|
||||
}
|
||||
else if (isNotInterface(bookHeadEnhancerClass)) {
|
||||
headGenerateClass = bookHeadEnhancerClass;
|
||||
}
|
||||
// 定义头信息增强则使用其生成头信息,否则使用 dataClass 来自动获取
|
||||
if (headGenerateClass != null) {
|
||||
fillCustomHeadInfo(dataClass, bookHeadEnhancerClass, writerSheetBuilder);
|
||||
}
|
||||
else if (dataClass != null) {
|
||||
writerSheetBuilder.head(dataClass);
|
||||
if (sheet.excludes().length > 0) {
|
||||
writerSheetBuilder.excludeColumnFiledNames(Arrays.asList(sheet.excludes()));
|
||||
}
|
||||
if (sheet.includes().length > 0) {
|
||||
writerSheetBuilder.includeColumnFiledNames(Arrays.asList(sheet.includes()));
|
||||
}
|
||||
}
|
||||
|
||||
// sheetBuilder 增强
|
||||
writerSheetBuilder = excelWriterBuilderEnhance.enhanceSheet(writerSheetBuilder, sheetNo, sheetName, dataClass,
|
||||
template, headGenerateClass);
|
||||
|
||||
return writerSheetBuilder.build();
|
||||
}
|
||||
|
||||
private void fillCustomHeadInfo(Class<?> dataClass, Class<? extends HeadGenerator> headEnhancerClass,
|
||||
ExcelWriterSheetBuilder writerSheetBuilder) {
|
||||
HeadGenerator headGenerator = this.applicationContext.getBean(headEnhancerClass);
|
||||
Assert.notNull(headGenerator, "The header generated bean does not exist.");
|
||||
HeadMeta head = headGenerator.head(dataClass);
|
||||
writerSheetBuilder.head(head.getHead());
|
||||
writerSheetBuilder.excludeColumnFiledNames(head.getIgnoreHeadFields());
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否为Null Head Generator
|
||||
* @param headGeneratorClass
|
||||
* @return true 已指定 false 未指定(默认值)
|
||||
*/
|
||||
private boolean isNotInterface(Class<? extends HeadGenerator> headGeneratorClass) {
|
||||
return !Modifier.isInterface(headGeneratorClass.getModifiers());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
|
||||
this.applicationContext = applicationContext;
|
||||
}
|
||||
|
||||
}
|
||||
+60
@@ -0,0 +1,60 @@
|
||||
package com.pig4cloud.pigx.common.excel.handler;
|
||||
|
||||
import com.alibaba.excel.context.AnalysisContext;
|
||||
import com.pig4cloud.pigx.common.excel.kit.Validators;
|
||||
import com.pig4cloud.pigx.common.excel.vo.ErrorMessage;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import javax.validation.ConstraintViolation;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 默认的 AnalysisEventListener
|
||||
*
|
||||
* @author lengleng
|
||||
* @author L.cm
|
||||
* @date 2021/4/16
|
||||
*/
|
||||
@Slf4j
|
||||
public class DefaultAnalysisEventListener extends ListAnalysisEventListener<Object> {
|
||||
|
||||
private final List<Object> list = new ArrayList<>();
|
||||
|
||||
private final List<ErrorMessage> errorMessageList = new ArrayList<>();
|
||||
|
||||
private Long lineNum = 1L;
|
||||
|
||||
@Override
|
||||
public void invoke(Object o, AnalysisContext analysisContext) {
|
||||
lineNum++;
|
||||
|
||||
Set<ConstraintViolation<Object>> violations = Validators.validate(o);
|
||||
if (!violations.isEmpty()) {
|
||||
Set<String> messageSet = violations.stream().map(ConstraintViolation::getMessage)
|
||||
.collect(Collectors.toSet());
|
||||
errorMessageList.add(new ErrorMessage(lineNum, messageSet));
|
||||
}
|
||||
else {
|
||||
list.add(o);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doAfterAllAnalysed(AnalysisContext analysisContext) {
|
||||
log.debug("Excel read analysed");
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Object> getList() {
|
||||
return list;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ErrorMessage> getErrors() {
|
||||
return errorMessageList;
|
||||
}
|
||||
|
||||
}
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
package com.pig4cloud.pigx.common.excel.handler;
|
||||
|
||||
import com.alibaba.excel.event.AnalysisEventListener;
|
||||
import com.pig4cloud.pigx.common.excel.vo.ErrorMessage;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* list analysis EventListener
|
||||
*
|
||||
* @author L.cm
|
||||
*/
|
||||
public abstract class ListAnalysisEventListener<T> extends AnalysisEventListener<T> {
|
||||
|
||||
/**
|
||||
* 获取 excel 解析的对象列表
|
||||
* @return 集合
|
||||
*/
|
||||
public abstract List<T> getList();
|
||||
|
||||
/**
|
||||
* 获取异常校验结果
|
||||
* @return 集合
|
||||
*/
|
||||
public abstract List<ErrorMessage> getErrors();
|
||||
|
||||
}
|
||||
+64
@@ -0,0 +1,64 @@
|
||||
package com.pig4cloud.pigx.common.excel.handler;
|
||||
|
||||
import com.alibaba.excel.ExcelWriter;
|
||||
import com.alibaba.excel.converters.Converter;
|
||||
import com.alibaba.excel.write.metadata.WriteSheet;
|
||||
import com.pig4cloud.pigx.common.excel.annotation.ResponseExcel;
|
||||
import com.pig4cloud.pigx.common.excel.annotation.Sheet;
|
||||
import com.pig4cloud.pigx.common.excel.config.ExcelConfigProperties;
|
||||
import com.pig4cloud.pigx.common.excel.enhance.WriterBuilderEnhancer;
|
||||
import com.pig4cloud.pigx.common.excel.kit.ExcelException;
|
||||
import lombok.SneakyThrows;
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author lengleng
|
||||
* @author L.cm
|
||||
* @date 2020/3/29
|
||||
*/
|
||||
public class ManySheetWriteHandler extends AbstractSheetWriteHandler {
|
||||
|
||||
public ManySheetWriteHandler(ExcelConfigProperties configProperties,
|
||||
ObjectProvider<List<Converter<?>>> converterProvider, WriterBuilderEnhancer excelWriterBuilderEnhance) {
|
||||
super(configProperties, converterProvider, excelWriterBuilderEnhance);
|
||||
}
|
||||
|
||||
/**
|
||||
* 当且仅当List不为空且List中的元素也是List 才返回true
|
||||
* @param obj 返回对象
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public boolean support(Object obj) {
|
||||
if (obj instanceof List) {
|
||||
List objList = (List) obj;
|
||||
return !objList.isEmpty() && objList.get(0) instanceof List;
|
||||
}
|
||||
else {
|
||||
throw new ExcelException("@ResponseExcel 返回值必须为List类型");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@SneakyThrows
|
||||
public void write(Object obj, HttpServletResponse response, ResponseExcel responseExcel) {
|
||||
List objList = (List) obj;
|
||||
ExcelWriter excelWriter = getExcelWriter(response, responseExcel);
|
||||
|
||||
Sheet[] sheets = responseExcel.sheets();
|
||||
WriteSheet sheet;
|
||||
for (int i = 0; i < sheets.length; i++) {
|
||||
List eleList = (List) objList.get(i);
|
||||
Class<?> dataClass = eleList.get(0).getClass();
|
||||
// 创建sheet
|
||||
sheet = this.sheet(sheets[i], dataClass, responseExcel.template(), responseExcel.headGenerator());
|
||||
// 写入sheet
|
||||
excelWriter.write(eleList, sheet);
|
||||
}
|
||||
excelWriter.finish();
|
||||
}
|
||||
|
||||
}
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
package com.pig4cloud.pigx.common.excel.handler;
|
||||
|
||||
import com.pig4cloud.pigx.common.excel.annotation.ResponseExcel;
|
||||
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
/**
|
||||
* @author lengleng
|
||||
* @date 2020/3/29
|
||||
* <p>
|
||||
* sheet 写出处理器
|
||||
*/
|
||||
public interface SheetWriteHandler {
|
||||
|
||||
/**
|
||||
* 是否支持
|
||||
* @param obj
|
||||
* @return
|
||||
*/
|
||||
boolean support(Object obj);
|
||||
|
||||
/**
|
||||
* 校验
|
||||
* @param responseExcel 注解
|
||||
*/
|
||||
void check(ResponseExcel responseExcel);
|
||||
|
||||
/**
|
||||
* 返回的对象
|
||||
* @param o obj
|
||||
* @param response 输出对象
|
||||
* @param responseExcel 注解
|
||||
*/
|
||||
void export(Object o, HttpServletResponse response, ResponseExcel responseExcel);
|
||||
|
||||
/**
|
||||
* 写成对象
|
||||
* @param o obj
|
||||
* @param response 输出对象
|
||||
* @param responseExcel 注解
|
||||
*/
|
||||
void write(Object o, HttpServletResponse response, ResponseExcel responseExcel);
|
||||
|
||||
}
|
||||
+60
@@ -0,0 +1,60 @@
|
||||
package com.pig4cloud.pigx.common.excel.handler;
|
||||
|
||||
import com.alibaba.excel.ExcelWriter;
|
||||
import com.alibaba.excel.converters.Converter;
|
||||
import com.alibaba.excel.write.metadata.WriteSheet;
|
||||
import com.pig4cloud.pigx.common.excel.annotation.ResponseExcel;
|
||||
import com.pig4cloud.pigx.common.excel.config.ExcelConfigProperties;
|
||||
import com.pig4cloud.pigx.common.excel.enhance.WriterBuilderEnhancer;
|
||||
import com.pig4cloud.pigx.common.excel.kit.ExcelException;
|
||||
import lombok.SneakyThrows;
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author lengleng
|
||||
* @author L.cm
|
||||
* @date 2020/3/29
|
||||
* <p>
|
||||
* 处理单sheet 页面
|
||||
*/
|
||||
public class SingleSheetWriteHandler extends AbstractSheetWriteHandler {
|
||||
|
||||
public SingleSheetWriteHandler(ExcelConfigProperties configProperties,
|
||||
ObjectProvider<List<Converter<?>>> converterProvider, WriterBuilderEnhancer excelWriterBuilderEnhance) {
|
||||
super(configProperties, converterProvider, excelWriterBuilderEnhance);
|
||||
}
|
||||
|
||||
/**
|
||||
* obj 是List 且list不为空同时list中的元素不是是List 才返回true
|
||||
* @param obj 返回对象
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public boolean support(Object obj) {
|
||||
if (obj instanceof List) {
|
||||
List objList = (List) obj;
|
||||
return !objList.isEmpty() && !(objList.get(0) instanceof List);
|
||||
}
|
||||
else {
|
||||
throw new ExcelException("@ResponseExcel 返回值必须为List类型");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@SneakyThrows
|
||||
public void write(Object obj, HttpServletResponse response, ResponseExcel responseExcel) {
|
||||
List list = (List) obj;
|
||||
ExcelWriter excelWriter = getExcelWriter(response, responseExcel);
|
||||
|
||||
// 有模板则不指定sheet名
|
||||
Class<?> dataClass = list.get(0).getClass();
|
||||
WriteSheet sheet = this.sheet(responseExcel.sheets()[0], dataClass, responseExcel.template(),
|
||||
responseExcel.headGenerator());
|
||||
excelWriter.write(list, sheet);
|
||||
excelWriter.finish();
|
||||
}
|
||||
|
||||
}
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
package com.pig4cloud.pigx.common.excel.head;
|
||||
|
||||
/**
|
||||
* Excel头生成器,用于自定义生成头部信息
|
||||
*
|
||||
* @author Hccake 2020/10/27
|
||||
* @version 1.0
|
||||
*/
|
||||
public interface HeadGenerator {
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* 自定义头部信息
|
||||
* </p>
|
||||
* 实现类根据数据的class信息,定制Excel头<br/>
|
||||
* 具体方法使用参考:https://www.yuque.com/easyexcel/doc/write#b4b9de00
|
||||
* @param clazz 当前sheet的数据类型
|
||||
* @return List<List<String>> Head头信息
|
||||
*/
|
||||
HeadMeta head(Class<?> clazz);
|
||||
|
||||
}
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
package com.pig4cloud.pigx.common.excel.head;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* @author Yakir
|
||||
* @date 2021/4/26 10:58
|
||||
*/
|
||||
@Data
|
||||
public class HeadMeta {
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* 自定义头部信息
|
||||
* </p>
|
||||
* 实现类根据数据的class信息,定制Excel头<br/>
|
||||
* 具体方法使用参考:https://www.yuque.com/easyexcel/doc/write#b4b9de00
|
||||
*/
|
||||
private List<List<String>> head;
|
||||
|
||||
/**
|
||||
* 忽略头对应字段名称
|
||||
*/
|
||||
private Set<String> ignoreHeadFields;
|
||||
|
||||
}
|
||||
+81
@@ -0,0 +1,81 @@
|
||||
package com.pig4cloud.pigx.common.excel.head;
|
||||
|
||||
import com.alibaba.excel.metadata.CellData;
|
||||
import com.alibaba.excel.metadata.Head;
|
||||
import com.alibaba.excel.write.handler.CellWriteHandler;
|
||||
import com.alibaba.excel.write.metadata.holder.WriteSheetHolder;
|
||||
import com.alibaba.excel.write.metadata.holder.WriteTableHolder;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.apache.commons.collections4.CollectionUtils;
|
||||
import org.apache.poi.ss.usermodel.Cell;
|
||||
import org.apache.poi.ss.usermodel.Row;
|
||||
import org.springframework.context.MessageSource;
|
||||
import org.springframework.context.i18n.LocaleContextHolder;
|
||||
import org.springframework.util.PropertyPlaceholderHelper;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 对表头进行国际化处理
|
||||
*
|
||||
* @author hccake
|
||||
*/
|
||||
@RequiredArgsConstructor
|
||||
public class I18nHeaderCellWriteHandler implements CellWriteHandler {
|
||||
|
||||
/**
|
||||
* 国际化消息源
|
||||
*/
|
||||
private final MessageSource messageSource;
|
||||
|
||||
/**
|
||||
* 国际化翻译
|
||||
*/
|
||||
private final PropertyPlaceholderHelper.PlaceholderResolver placeholderResolver;
|
||||
|
||||
public I18nHeaderCellWriteHandler(MessageSource messageSource) {
|
||||
this.messageSource = messageSource;
|
||||
this.placeholderResolver = placeholderName -> messageSource.getMessage(placeholderName, null,
|
||||
LocaleContextHolder.getLocale());
|
||||
}
|
||||
|
||||
/**
|
||||
* 占位符处理
|
||||
*/
|
||||
private final PropertyPlaceholderHelper propertyPlaceholderHelper = new PropertyPlaceholderHelper("{", "}");
|
||||
|
||||
@Override
|
||||
public void beforeCellCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Row row,
|
||||
Head head, Integer columnIndex, Integer relativeRowIndex, Boolean isHead) {
|
||||
if (isHead) {
|
||||
List<String> originHeadNameList = head.getHeadNameList();
|
||||
if (CollectionUtils.isNotEmpty(originHeadNameList)) {
|
||||
// 国际化处理
|
||||
List<String> i18nHeadNames = originHeadNameList.stream()
|
||||
.map(headName -> propertyPlaceholderHelper.replacePlaceholders(headName, placeholderResolver))
|
||||
.collect(Collectors.toList());
|
||||
head.setHeadNameList(i18nHeadNames);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterCellCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Cell cell,
|
||||
Head head, Integer relativeRowIndex, Boolean isHead) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterCellDataConverted(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder,
|
||||
CellData cellData, Cell cell, Head head, Integer relativeRowIndex, Boolean isHead) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterCellDispose(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder,
|
||||
List<CellData> cellDataList, Cell cell, Head head, Integer relativeRowIndex, Boolean isHead) {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
package com.pig4cloud.pigx.common.excel.kit;
|
||||
|
||||
/**
|
||||
* @author lengleng
|
||||
* @date 2020/3/31
|
||||
*/
|
||||
public class ExcelException extends RuntimeException {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public ExcelException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
}
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
package com.pig4cloud.pigx.common.excel.kit;
|
||||
|
||||
import javax.validation.*;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* 校验工具
|
||||
*
|
||||
* @author L.cm
|
||||
*/
|
||||
public class Validators {
|
||||
|
||||
private static final Validator validator;
|
||||
|
||||
static {
|
||||
ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
|
||||
validator = factory.getValidator();
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates all constraints on {@code object}.
|
||||
* @param object object to validate
|
||||
* @param <T> the type of the object to validate
|
||||
* @return constraint violations or an empty set if none
|
||||
* @throws IllegalArgumentException if object is {@code null} or if {@code null} is
|
||||
* passed to the varargs groups
|
||||
* @throws ValidationException if a non recoverable error happens during the
|
||||
* validation process
|
||||
*/
|
||||
public static <T> Set<ConstraintViolation<T>> validate(T object) {
|
||||
return validator.validate(object);
|
||||
}
|
||||
|
||||
}
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
package com.pig4cloud.pigx.common.excel.processor;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
/**
|
||||
* @author lengleng
|
||||
* @date 2020/3/29
|
||||
*/
|
||||
public interface NameProcessor {
|
||||
|
||||
/**
|
||||
* 解析名称
|
||||
* @param args 拦截器对象
|
||||
* @param method
|
||||
* @param key 表达式
|
||||
* @return
|
||||
*/
|
||||
String doDetermineName(Object[] args, Method method, String key);
|
||||
|
||||
}
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
package com.pig4cloud.pigx.common.excel.processor;
|
||||
|
||||
import org.springframework.context.expression.MethodBasedEvaluationContext;
|
||||
import org.springframework.core.DefaultParameterNameDiscoverer;
|
||||
import org.springframework.core.ParameterNameDiscoverer;
|
||||
import org.springframework.expression.EvaluationContext;
|
||||
import org.springframework.expression.ExpressionParser;
|
||||
import org.springframework.expression.spel.standard.SpelExpressionParser;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
/**
|
||||
* @author lengleng
|
||||
* @date 2020/3/29
|
||||
*/
|
||||
public class NameSpelExpressionProcessor implements NameProcessor {
|
||||
|
||||
/**
|
||||
* 参数发现器
|
||||
*/
|
||||
private static final ParameterNameDiscoverer NAME_DISCOVERER = new DefaultParameterNameDiscoverer();
|
||||
|
||||
/**
|
||||
* Express语法解析器
|
||||
*/
|
||||
private static final ExpressionParser PARSER = new SpelExpressionParser();
|
||||
|
||||
@Override
|
||||
public String doDetermineName(Object[] args, Method method, String key) {
|
||||
|
||||
if (!key.contains("#")) {
|
||||
return key;
|
||||
}
|
||||
|
||||
EvaluationContext context = new MethodBasedEvaluationContext(null, method, args, NAME_DISCOVERER);
|
||||
final Object value = PARSER.parseExpression(key).getValue(context);
|
||||
return value == null ? null : value.toString();
|
||||
}
|
||||
|
||||
}
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
package com.pig4cloud.pigx.common.excel.vo;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* 校验错误信息
|
||||
*
|
||||
* @author lengleng
|
||||
* @date 2021/8/4
|
||||
*/
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class ErrorMessage {
|
||||
|
||||
/**
|
||||
* 行号
|
||||
*/
|
||||
private Long lineNum;
|
||||
|
||||
/**
|
||||
* 错误信息
|
||||
*/
|
||||
private Set<String> errors = new HashSet<>();
|
||||
|
||||
public ErrorMessage(Set<String> errors) {
|
||||
this.errors = errors;
|
||||
}
|
||||
|
||||
public ErrorMessage(String error) {
|
||||
HashSet<String> objects = new HashSet<>();
|
||||
objects.add(error);
|
||||
this.errors = objects;
|
||||
}
|
||||
|
||||
}
|
||||
Reference in new issue
Block a user