Skip to content

Commit

Permalink
Merge pull request #403 from liaochong/feature/4.3.3
Browse files Browse the repository at this point in the history
Feature/4.3.3
  • Loading branch information
liaochong authored Nov 10, 2023
2 parents 35ecd88 + 29f041b commit 72be97c
Show file tree
Hide file tree
Showing 14 changed files with 350 additions and 38 deletions.
2 changes: 1 addition & 1 deletion example/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@
<dependency>
<groupId>com.github.liaochong</groupId>
<artifactId>myexcel</artifactId>
<version>4.3.0.RC5</version>
<version>4.3.3</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
Expand Down
13 changes: 10 additions & 3 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@

<groupId>com.github.liaochong</groupId>
<artifactId>myexcel</artifactId>
<version>4.3.2</version>
<version>4.3.3</version>
<packaging>jar</packaging>

<name>myexcel</name>
Expand All @@ -22,8 +22,8 @@
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
<poi.version>5.2.3</poi.version>
<jsoup.version>1.16.1</jsoup.version>
<poi.version>5.2.4</poi.version>
<jsoup.version>1.16.2</jsoup.version>
<lombok.version>1.18.22</lombok.version>
<beetl.version>3.15.4.RELEASE</beetl.version>
<velocity.version>2.3</velocity.version>
Expand All @@ -32,6 +32,7 @@
<thymeleaf.version>3.1.1.RELEASE</thymeleaf.version>
<enjoy.version>5.0.3</enjoy.version>
<javax.servlet-api.version>4.0.1</javax.servlet-api.version>
<jakarta.servlet-api.version>5.0.0</jakarta.servlet-api.version>
<junit-jupiter-api.version>5.8.2</junit-jupiter-api.version>
<imageio-jpeg.version>3.9.4</imageio-jpeg.version>
<commons-csv.version>1.10.0</commons-csv.version>
Expand Down Expand Up @@ -111,6 +112,12 @@
<version>${javax.servlet-api.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>jakarta.servlet</groupId>
<artifactId>jakarta.servlet-api</artifactId>
<version>${jakarta.servlet-api.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -495,20 +495,49 @@ protected List<FieldDefinition> getPreElectionFields(ClassFieldContainer classFi
} else {
if (configuration.excludeParent) {
preElectionFields = classFieldContainer.getDeclaredFields().stream()
.filter(fieldDefinition -> fieldDefinition.getField().isAnnotationPresent(ExcelColumn.class))
.filter(fieldDefinition ->
fieldDefinition.getField().isAnnotationPresent(ExcelColumn.class)
|| fieldDefinition.getField().isAnnotationPresent(MultiColumn.class))
.collect(Collectors.toList());
} else {
preElectionFields = classFieldContainer.getFieldsByAnnotation(ExcelColumn.class);
preElectionFields = classFieldContainer.getFieldsByAnnotation(ExcelColumn.class, MultiColumn.class);
}
}
if (configuration.ignoreStaticFields) {
preElectionFields = preElectionFields.stream()
.filter(fieldDefinition -> !Modifier.isStatic(fieldDefinition.getField().getModifiers()))
.collect(Collectors.toList());
}
this.extractedMultiFieldDefinitions(preElectionFields);
return preElectionFields;
}

/**
* 提取嵌套的MultiColumn中导出列到顶层
*
* @param preElectionFields 预选字段
*/
private void extractedMultiFieldDefinitions(List<FieldDefinition> preElectionFields) {
List<FieldDefinition> multiFieldDefinitions = preElectionFields.stream()
.filter(preElectionField -> preElectionField.getField().isAnnotationPresent(MultiColumn.class) && preElectionField.getField().getType() != List.class)
.flatMap(preElectionField ->
// 这种方式会导致缺失第一个父字段,需要补全
ReflectUtil.getWriteFieldDefinitionsOfExcelColumn(preElectionField.getField().getType()).stream()
.peek(fieldDefinition -> {
if (fieldDefinition.getParentFields() == null || fieldDefinition.getParentFields().isEmpty()) {
fieldDefinition.setParentFields(Collections.singletonList(preElectionField.getField()));
} else {
fieldDefinition.getParentFields().add(0, preElectionField.getField());
}
})
).collect(Collectors.toList());
if (multiFieldDefinitions.isEmpty()) {
return;
}
preElectionFields.removeIf(preElectionField -> preElectionField.getField().isAnnotationPresent(MultiColumn.class) && preElectionField.getField().getType() != List.class);
preElectionFields.addAll(multiFieldDefinitions);
}

/**
* 展示字段order与标题title长度一致性自适应
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,14 +75,38 @@ public static synchronized void registering(WriteConverter... writeConverters) {
}

public static Pair<? extends Class, Object> convert(FieldDefinition fieldDefinition, Object object, ConvertContext convertContext) {
Object result = ReflectUtil.getFieldValue(object, fieldDefinition);
Object result = getFieldVal(fieldDefinition, object);
if (result == null) {
return Constants.NULL_PAIR;
}
WriteConverter writeConverter = getWriteConverter(fieldDefinition.getField(), fieldDefinition.getField().getType(), result, convertContext, WRITE_CONVERTER_CONTAINER);
return writeConverter.convert(fieldDefinition.getField(), fieldDefinition.getField().getType(), result, convertContext);
}

private static Object getFieldVal(FieldDefinition fieldDefinition, Object object) {
Object result;
if (fieldDefinition.getParentFields() == null
|| fieldDefinition.getParentFields().isEmpty()) {
result = ReflectUtil.getFieldValue(object, fieldDefinition);
} else {
Object prevObj;
try {
prevObj = object;
for (int i = 0, size = fieldDefinition.getParentFields().size(); i < size; i++) {
Field parentField = fieldDefinition.getParentFields().get(i);
prevObj = parentField.get(prevObj);
}
if (prevObj == null) {
return null;
}
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
result = ReflectUtil.getFieldValue(prevObj, fieldDefinition);
}
return result;
}

public static WriteConverter getWriteConverter(Field field, Class<?> fieldType, Object result, ConvertContext convertContext, List<Pair<Class, WriteConverter>> writeConverterContainer) {
WriteConverter writeConverter = convertContext.isConvertCsv ? CSV_CONVERTER_CACHE.get(Pair.of(field, fieldType)) : EXCEL_CONVERTER_CACHE.get(Pair.of(field, fieldType));
if (writeConverter != null) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
/*
* Copyright 2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.github.liaochong.myexcel.utils;

import com.github.liaochong.myexcel.core.constant.Constants;
import jakarta.servlet.http.HttpServletResponse;
import org.apache.commons.codec.CharEncoding;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
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.Workbook;
import org.apache.poi.xssf.streaming.SXSSFWorkbook;

import java.io.IOException;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.nio.file.Files;
import java.nio.file.Path;
import java.security.GeneralSecurityException;

/**
* 附件导出工具类
*
* @author liaochong
* @version 1.0
*/
public final class AttachmentV2ExportUtil {

/**
* 导出
*
* @param workbook workbook
* @param fileName file name,suffix is not required,and it is not recommended to carry a suffix
* @param response HttpServletResponse
*/
public static void export(Workbook workbook, String fileName, HttpServletResponse response) {
try {
String suffix = Constants.XLSX;
if (workbook instanceof HSSFWorkbook) {
if (fileName.endsWith(suffix)) {
fileName = fileName.substring(0, fileName.length() - 1);
}
suffix = Constants.XLS;
response.setContentType("application/vnd.ms-excel");
} else {
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
}
if (!fileName.endsWith(suffix)) {
fileName += suffix;
}
setAttachmentConfig(fileName, response);
workbook.write(response.getOutputStream());
} catch (IOException e) {
throw new RuntimeException(e);
} finally {
clear(workbook);
}
}

/**
* 加密导出
*
* @param workbook workbook
* @param fileName fileName
* @param response response
* @param password password
*/
public static void encryptExport(final Workbook workbook, String fileName, HttpServletResponse response, final String password) {
if (workbook instanceof HSSFWorkbook) {
throw new IllegalArgumentException("Document encryption for.xls is not supported");
}
Path path = null;
try {
String suffix = Constants.XLSX;
path = TempFileOperator.createTempFile("encrypt_temp", suffix);
workbook.write(Files.newOutputStream(path));

final POIFSFileSystem fs = new POIFSFileSystem();
final EncryptionInfo info = new EncryptionInfo(EncryptionMode.standard);
final Encryptor enc = info.getEncryptor();
enc.confirmPassword(password);

try (OPCPackage opc = OPCPackage.open(path.toFile(), PackageAccess.READ_WRITE);
OutputStream os = enc.getDataStream(fs)) {
opc.save(os);
}
if (!fileName.endsWith(suffix)) {
fileName += suffix;
}
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
setAttachmentConfig(fileName, response);
fs.writeFilesystem(response.getOutputStream());
} catch (IOException | InvalidFormatException | GeneralSecurityException e) {
throw new RuntimeException(e);
} finally {
clear(workbook);
TempFileOperator.deleteTempFile(path);
}
}

private static void clear(Workbook workbook) {
if (workbook instanceof SXSSFWorkbook) {
((SXSSFWorkbook) workbook).dispose();
}
try {
workbook.close();
} catch (IOException e) {
e.printStackTrace();
}
}

/**
* 一般文件导出接口
*
* @param path 文件
* @param fileName 导出后文件名称
* @param response 响应流
*/
public static void export(Path path, String fileName, HttpServletResponse response) {
try {
response.setContentType("application/octet-stream");
setAttachmentConfig(fileName, response);
response.getOutputStream().write(Files.readAllBytes(path));
} catch (IOException e) {
throw new RuntimeException(e);
} finally {
TempFileOperator.deleteTempFile(path);
}
}

private static void setAttachmentConfig(String fileName, HttpServletResponse response) throws UnsupportedEncodingException {
response.setCharacterEncoding(CharEncoding.UTF_8);
response.addHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileName, CharEncoding.UTF_8).replace("+", "%20"));
}
}
51 changes: 51 additions & 0 deletions src/main/java/com/github/liaochong/myexcel/utils/ReflectUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
Expand Down Expand Up @@ -60,6 +61,56 @@ public static ClassFieldContainer getAllFieldsOfClass(Class<?> clazz) {
return container;
}

public static List<FieldDefinition> getWriteFieldDefinitionsOfExcelColumn(Class<?> dataType) {
if (dataType == Map.class) {
return Collections.emptyList();
}
List<FieldDefinition> fieldDefinitions = new ArrayList<>();
getWriteFieldDefinition(dataType, fieldDefinitions, null, 0);
return fieldDefinitions;
}

private static void getWriteFieldDefinition(Class<?> dataType, List<FieldDefinition> fieldDefinitions, List<Field> parentFields, int level) {
ClassFieldContainer classFieldContainer = ReflectUtil.getAllFieldsOfClass(dataType);
List<FieldDefinition> fields = classFieldContainer.getFieldsByAnnotation(ExcelColumn.class, MultiColumn.class);
if (level == 0 && fields.isEmpty()) {
// If no field contains an ExcelColumn annotation, all fields are read in the default order
List<FieldDefinition> allFields = classFieldContainer.getFields();
for (int i = 0, size = allFields.size(); i < size; i++) {
fieldDefinitions.add(allFields.get(i));
}
} else {
List<Field> topParentFields = new LinkedList<>();
if (parentFields != null) {
topParentFields.addAll(parentFields);
}
for (FieldDefinition fieldDefinition : fields) {
if (level == 0) {
parentFields = new LinkedList<>();
}
Field field = fieldDefinition.getField();
if (field.isAnnotationPresent(MultiColumn.class)) {
MultiColumn multiColumn = field.getAnnotation(MultiColumn.class);
List<Field> childrenParentFields = new LinkedList<>(topParentFields);
childrenParentFields.add(field);
if (ReadConverterContext.support(multiColumn.classType())) {
field.setAccessible(true);
fieldDefinition = new FieldDefinition(field);
fieldDefinition.setParentFields(parentFields.isEmpty() ? Collections.emptyList() : parentFields);
fieldDefinitions.add(fieldDefinition);
} else {
getWriteFieldDefinition(multiColumn.classType(), fieldDefinitions, childrenParentFields, level + 1);
}
} else {
field.setAccessible(true);
fieldDefinition = new FieldDefinition(field);
fieldDefinition.setParentFields(parentFields.isEmpty() ? Collections.emptyList() : parentFields);
fieldDefinitions.add(fieldDefinition);
}
}
}
}

public static Map<Integer, FieldDefinition> getFieldDefinitionMapOfExcelColumn(Class<?> dataType) {
if (dataType == Map.class) {
return Collections.emptyMap();
Expand Down
Loading

0 comments on commit 72be97c

Please sign in to comment.