├── _config.yml ├── .gitignore ├── src └── main │ └── java │ └── com │ └── excel │ └── poi │ ├── entity │ ├── ExcelEntity.java │ ├── ErrorEntity.java │ └── ExcelPropertyEntity.java │ ├── exception │ ├── AllEmptyRowException.java │ └── ExcelBootException.java │ ├── function │ ├── ImportFunction.java │ └── ExportFunction.java │ ├── common │ ├── RegexUtil.java │ ├── Constant.java │ ├── DateFormatUtil.java │ ├── RegexConst.java │ └── StringUtil.java │ ├── annotation │ ├── ExportField.java │ └── ImportField.java │ ├── factory │ └── ExcelMappingFactory.java │ ├── excel │ ├── ExcelWriter.java │ └── ExcelReader.java │ └── ExcelBoot.java ├── pom.xml ├── LICENSE └── README.md /_config.yml: -------------------------------------------------------------------------------- 1 | theme: jekyll-theme-merlot -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | #eclipse files 2 | .classpath 3 | .project 4 | 5 | /bin/ 6 | /target/ 7 | /.settings/ 8 | 9 | #idea files 10 | target/ 11 | *.iml 12 | *.ipr 13 | *.iws 14 | .idea/ -------------------------------------------------------------------------------- /src/main/java/com/excel/poi/entity/ExcelEntity.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 NingWei (ningww1@126.com) 3 | *

4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | *

16 | */ 17 | package com.excel.poi.entity; 18 | 19 | import java.util.List; 20 | import lombok.Getter; 21 | import lombok.Setter; 22 | 23 | /** 24 | * @author NingWei 25 | */ 26 | @Setter 27 | @Getter 28 | public class ExcelEntity { 29 | 30 | private String fileName; 31 | private List propertyList; 32 | } -------------------------------------------------------------------------------- /src/main/java/com/excel/poi/exception/AllEmptyRowException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 NingWei (ningww1@126.com) 3 | *

4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | *

16 | */ 17 | 18 | package com.excel.poi.exception; 19 | 20 | import org.slf4j.helpers.MessageFormatter; 21 | 22 | /** 23 | * @author NingWei 24 | */ 25 | public class AllEmptyRowException extends RuntimeException { 26 | private static final long serialVersionUID = 1L; 27 | 28 | public AllEmptyRowException(String format, Object... arguments) { 29 | super(MessageFormatter.arrayFormat(format, arguments).getMessage()); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/com/excel/poi/entity/ErrorEntity.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 NingWei (ningww1@126.com) 3 | *

4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | *

16 | */ 17 | package com.excel.poi.entity; 18 | 19 | import lombok.Builder; 20 | import lombok.Getter; 21 | import lombok.Setter; 22 | 23 | /** 24 | * @author NingWei 25 | */ 26 | @Setter 27 | @Getter 28 | @Builder 29 | public class ErrorEntity { 30 | private Integer sheetIndex; 31 | private Integer rowIndex; 32 | private Integer cellIndex; 33 | private String cellValue; 34 | private String columnName; 35 | private String errorMessage; 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/com/excel/poi/function/ImportFunction.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 NingWei (ningww1@126.com) 3 | *

4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | *

16 | */ 17 | 18 | package com.excel.poi.function; 19 | 20 | import com.excel.poi.entity.ErrorEntity; 21 | 22 | /** 23 | * @author NingWei 24 | */ 25 | public interface ImportFunction { 26 | 27 | /** 28 | * 导入常规校验通过后执行的操作 29 | * 30 | * @param sheetIndex 31 | * @param rowIndex 32 | * @param entity 33 | */ 34 | void onProcess(int sheetIndex, int rowIndex, T entity); 35 | 36 | /** 37 | * 导入常规校验失败后执行的操作 38 | * 39 | * @param errorEntity 40 | */ 41 | void onError(ErrorEntity errorEntity); 42 | } -------------------------------------------------------------------------------- /src/main/java/com/excel/poi/function/ExportFunction.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 NingWei (ningww1@126.com) 3 | *

4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | *

16 | */ 17 | package com.excel.poi.function; 18 | 19 | import java.util.List; 20 | 21 | /** 22 | * 分页查询 23 | * 24 | * @param

25 | * @param 26 | * @author NingWei 27 | */ 28 | public interface ExportFunction { 29 | /** 30 | * 分页查询方法 31 | * 32 | * @param param 33 | * @param pageNum 34 | * @param pageSize 35 | * @return 36 | */ 37 | List pageQuery(P param, int pageNum, int pageSize); 38 | 39 | /** 40 | * 集合内对象转换 41 | * 42 | * @param queryResult 43 | * @return 44 | */ 45 | Object convert(T queryResult); 46 | 47 | } 48 | -------------------------------------------------------------------------------- /src/main/java/com/excel/poi/exception/ExcelBootException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 NingWei (ningww1@126.com) 3 | *

4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | *

16 | */ 17 | package com.excel.poi.exception; 18 | 19 | import org.slf4j.helpers.MessageFormatter; 20 | 21 | /** 22 | * @author NingWei 23 | */ 24 | public class ExcelBootException extends RuntimeException { 25 | private static final long serialVersionUID = 1L; 26 | 27 | public ExcelBootException(String msg) { 28 | super(msg); 29 | } 30 | 31 | public ExcelBootException(String format, Object... arguments) { 32 | super(MessageFormatter.arrayFormat(format, arguments).getMessage()); 33 | } 34 | 35 | public ExcelBootException(Throwable cause, String format, Object... arguments) { 36 | super(MessageFormatter.arrayFormat(format, arguments).getMessage(), cause); 37 | } 38 | 39 | public ExcelBootException(Throwable cause) { 40 | super(cause); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/com/excel/poi/common/RegexUtil.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 NingWei (ningww1@126.com) 3 | *

4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | *

16 | */ 17 | package com.excel.poi.common; 18 | 19 | import com.google.common.cache.CacheBuilder; 20 | import com.google.common.cache.CacheLoader; 21 | import com.google.common.cache.LoadingCache; 22 | import java.util.concurrent.ExecutionException; 23 | import java.util.regex.Pattern; 24 | 25 | /** 26 | * @author NingWei 27 | */ 28 | public class RegexUtil { 29 | 30 | private final static LoadingCache LOAD_CACHE = 31 | CacheBuilder.newBuilder() 32 | .maximumSize(30) 33 | .build(new CacheLoader() { 34 | @Override 35 | public Pattern load(String pattern) { 36 | return Pattern.compile(pattern); 37 | } 38 | }); 39 | 40 | public static Boolean isMatch(String pattern, String value) throws ExecutionException { 41 | return LOAD_CACHE.get(pattern).matcher(value).matches(); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/main/java/com/excel/poi/common/Constant.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 NingWei (ningww1@126.com) 3 | *

4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | *

16 | */ 17 | package com.excel.poi.common; 18 | 19 | /** 20 | * @author NingWei 21 | */ 22 | public final class Constant { 23 | /** 24 | * Excel自动刷新到磁盘的数量 25 | */ 26 | public static final int DEFAULT_ROW_ACCESS_WINDOW_SIZE = 2000; 27 | /** 28 | * 分页条数 29 | */ 30 | public static final int DEFAULT_PAGE_SIZE = 3000; 31 | /** 32 | * 分Sheet条数 33 | */ 34 | public static final int DEFAULT_RECORD_COUNT_PEER_SHEET = 80000; 35 | /** 36 | * 是否开启自动适配宽度 37 | */ 38 | public static final boolean OPEN_AUTO_COLUM_WIDTH = true; 39 | 40 | public static final String CELL = "c"; 41 | public static final String XYZ_LOCATION = "r"; 42 | public static final String CELL_T_PROPERTY = "t"; 43 | public static final String CELL_S_VALUE = "s"; 44 | public static final String ROW = "row"; 45 | 46 | public static final int CHINESES_ATUO_SIZE_COLUMN_WIDTH_MAX = 60; 47 | public static final int CHINESES_ATUO_SIZE_COLUMN_WIDTH_MIN = 15; 48 | 49 | public static final int MAX_RECORD_COUNT_PEER_SHEET = 1000000; 50 | } 51 | -------------------------------------------------------------------------------- /src/main/java/com/excel/poi/annotation/ExportField.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 NingWei (ningww1@126.com) 3 | *

4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | *

16 | */ 17 | package com.excel.poi.annotation; 18 | 19 | import java.lang.annotation.ElementType; 20 | import java.lang.annotation.Retention; 21 | import java.lang.annotation.RetentionPolicy; 22 | import java.lang.annotation.Target; 23 | import java.math.BigDecimal; 24 | 25 | /** 26 | * @author NingWei 27 | */ 28 | @Retention(RetentionPolicy.RUNTIME) 29 | @Target(ElementType.FIELD) 30 | public @interface ExportField { 31 | 32 | /** 33 | * excel列名称 34 | * 35 | * @return 36 | */ 37 | String columnName(); 38 | 39 | /** 40 | * 默认单元格值 41 | * 42 | * @return 43 | */ 44 | String defaultCellValue() default ""; 45 | 46 | /** 47 | * 日期格式 默认 yyyy-MM-dd HH:mm:ss 48 | * 49 | * @return 50 | */ 51 | String dateFormat() default "yyyy-MM-dd HH:mm:ss"; 52 | 53 | /** 54 | * BigDecimal精度 默认:-1(默认不开启BigDecimal格式化) 55 | * 56 | * @return 57 | */ 58 | int scale() default -1; 59 | 60 | /** 61 | * BigDecimal 舍入规则 默认:BigDecimal.ROUND_HALF_EVEN 62 | * 63 | * @return 64 | */ 65 | int roundingMode() default BigDecimal.ROUND_HALF_EVEN; 66 | } 67 | -------------------------------------------------------------------------------- /src/main/java/com/excel/poi/annotation/ImportField.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 NingWei (ningww1@126.com) 3 | *

4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | *

16 | */ 17 | package com.excel.poi.annotation; 18 | 19 | import java.lang.annotation.ElementType; 20 | import java.lang.annotation.Retention; 21 | import java.lang.annotation.RetentionPolicy; 22 | import java.lang.annotation.Target; 23 | import java.math.BigDecimal; 24 | 25 | /** 26 | * @author NingWei 27 | */ 28 | @Retention(RetentionPolicy.RUNTIME) 29 | @Target(ElementType.FIELD) 30 | public @interface ImportField { 31 | 32 | /** 33 | * @return 是否必填 34 | */ 35 | boolean required() default false; 36 | 37 | /** 38 | * 日期格式 默认 yyyy-MM-dd HH:mm:ss 39 | * 40 | * @return 41 | */ 42 | String dateFormat() default "yyyy-MM-dd HH:mm:ss"; 43 | 44 | /** 45 | * 正则表达式校验 46 | * 47 | * @return 48 | */ 49 | String regex() default ""; 50 | 51 | /** 52 | * 正则表达式校验失败返回的错误信息,regex配置后生效 53 | * 54 | * @return 55 | */ 56 | String regexMessage() default "正则表达式验证失败"; 57 | 58 | /** 59 | * BigDecimal精度 默认:2 60 | * 61 | * @return 62 | */ 63 | int scale() default 2; 64 | 65 | /** 66 | * BigDecimal 舍入规则 默认:BigDecimal.ROUND_HALF_EVEN 67 | * 68 | * @return 69 | */ 70 | int roundingMode() default BigDecimal.ROUND_HALF_EVEN; 71 | } 72 | -------------------------------------------------------------------------------- /src/main/java/com/excel/poi/common/DateFormatUtil.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 NingWei (ningww1@126.com) 3 | *

4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | *

16 | */ 17 | package com.excel.poi.common; 18 | 19 | import com.google.common.cache.CacheBuilder; 20 | import com.google.common.cache.CacheLoader; 21 | import com.google.common.cache.LoadingCache; 22 | import java.text.ParseException; 23 | import java.text.SimpleDateFormat; 24 | import java.util.Date; 25 | import java.util.concurrent.ExecutionException; 26 | 27 | /** 28 | * @author NingWei 29 | */ 30 | public class DateFormatUtil { 31 | 32 | private final static LoadingCache LOAD_CACHE = 33 | CacheBuilder.newBuilder() 34 | .maximumSize(5) 35 | .build(new CacheLoader() { 36 | @Override 37 | public SimpleDateFormat load(String pattern) { 38 | return new SimpleDateFormat(pattern); 39 | } 40 | }); 41 | 42 | public static Date parse(String pattern, String value) throws ExecutionException, ParseException { 43 | return LOAD_CACHE.get(pattern).parse(value); 44 | } 45 | 46 | public static String format(String pattern, Date value) throws ExecutionException { 47 | return LOAD_CACHE.get(pattern).format(value); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/main/java/com/excel/poi/entity/ExcelPropertyEntity.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 NingWei (ningww1@126.com) 3 | *

4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | *

16 | */ 17 | package com.excel.poi.entity; 18 | 19 | import java.lang.reflect.Field; 20 | import lombok.Builder; 21 | import lombok.Getter; 22 | import lombok.Setter; 23 | 24 | /** 25 | * @author NingWei 26 | */ 27 | @Getter 28 | @Setter 29 | @Builder 30 | public class ExcelPropertyEntity { 31 | 32 | /** 33 | * excelModel字段Field 34 | */ 35 | private Field fieldEntity; 36 | /** 37 | * excel列名称 38 | * 39 | * @return 40 | */ 41 | private String columnName; 42 | /** 43 | * 默认单元格值 44 | * 45 | * @return 46 | */ 47 | private String templateCellValue; 48 | /** 49 | * 日期格式 默认 yyyy-MM-dd HH:mm:ss 50 | * 51 | * @return 52 | */ 53 | private String dateFormat; 54 | /** 55 | * 正则表达式校验 56 | * 57 | * @return 58 | */ 59 | private String regex; 60 | /** 61 | * 正则表达式校验失败返回的错误信息,regex配置后生效 62 | * 63 | * @return 64 | */ 65 | private String regexMessage; 66 | /** 67 | * BigDecimal精度 默认:2 68 | * 69 | * @return 70 | */ 71 | private Integer scale; 72 | /** 73 | * BigDecimal 舍入规则 默认:2 74 | * 75 | * @return 76 | */ 77 | private Integer roundingMode; 78 | /** 79 | * @return 是否必填 80 | */ 81 | private Boolean required; 82 | } 83 | -------------------------------------------------------------------------------- /src/main/java/com/excel/poi/common/RegexConst.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 NingWei (ningww1@126.com) 3 | *

4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | *

16 | */ 17 | package com.excel.poi.common; 18 | 19 | /** 20 | * @author NingWei 21 | */ 22 | public class RegexConst { 23 | /** 24 | * 金额校验:支持正负,整数位不大于12位,小数位不大于2位,也可以没有小数位 25 | */ 26 | public static final String AMOUNT_REGEX = "((-|\\+?)\\d{1,12}|(-|\\+?)\\d{1,12}\\.\\d{1,2})"; 27 | /** 28 | * 手机号验证:共11位,1开头 29 | */ 30 | public static final String PHONE_REGEX = "^(1)\\d{10}$"; 31 | /** 32 | * 邮箱验证 33 | */ 34 | public static final String MAIL_REGEX = "^[\\w-]+(\\.[\\w-]+)*@[\\w-]+(\\.[\\w-]+)+$"; 35 | /** 36 | * 身份证校验 37 | */ 38 | public static final String IDCARD_REGEX = "(^[1-9]\\d{9}((0[1-9])|(10|11|12))(([0-2][1-9])|10|20|30|31)\\d{3}[0-9Xx]$)|" + 39 | "(^[1-9]\\d{9}((0[1-9])|(10|11|12))(([0-2][1-9])|10|20|30|31)\\d{3}$)"; 40 | //^开头 41 | //[1-9] 第一位1-9中的一个 4 42 | //\\d{9} 五位数字 (省市县地区+出生年份) 43 | //((0[1-9])|(10|11|12)) 01(月份) 44 | //(([0-2][1-9])|10|20|30|31)01(日期) 45 | //\\d{3} 三位数字 123(第十七位奇数代表男,偶数代表女) 46 | //[0-9Xx] 0123456789Xx其中的一个 X(第十八位为校验值) 47 | //$结尾 48 | 49 | //^开头 50 | //[1-9] 第一位1-9中的一个 4 51 | //\\d{9} 五位数字 (省市县地区+出生年份) 52 | //((0[1-9])|(10|11|12)) 01(月份) 53 | //(([0-2][1-9])|10|20|30|31)01(日期) 54 | //\\d{3} 三位数字 123(第十五位奇数代表男,偶数代表女),15位身份证不含X 55 | //$结尾 56 | } 57 | -------------------------------------------------------------------------------- /src/main/java/com/excel/poi/common/StringUtil.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 NingWei (ningww1@126.com) 3 | *

4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | *

16 | */ 17 | package com.excel.poi.common; 18 | 19 | /** 20 | * @author NingWei 21 | */ 22 | public class StringUtil { 23 | /** 24 | * 判断字符串是否为空 25 | * 26 | * @param str 27 | * @return 28 | */ 29 | public static boolean isBlank(Object str) { 30 | return str == null || "".equals(str.toString().trim()) || "null".equalsIgnoreCase(str.toString().trim()); 31 | } 32 | 33 | /** 34 | * 格式化null为空 35 | * 36 | * @param str 37 | * @return 38 | */ 39 | public static String convertNull(Object str) { 40 | if (str == null) { 41 | return ""; 42 | } 43 | return str.toString(); 44 | } 45 | 46 | /** 47 | * 格式化null为0 48 | * 49 | * @param str 50 | * @return 51 | */ 52 | public static String convertNullTOZERO(Object str) { 53 | if (str == null || "".equals(str.toString().trim()) || "null".equalsIgnoreCase(str.toString().trim())) { 54 | return "0"; 55 | } 56 | return str.toString(); 57 | } 58 | 59 | public static boolean isNumeric(CharSequence cs) { 60 | if (isBlank(cs)) { 61 | return false; 62 | } else { 63 | int sz = cs.length(); 64 | 65 | for (int i = 0; i < sz; ++i) { 66 | if (!Character.isDigit(cs.charAt(i))) { 67 | return false; 68 | } 69 | } 70 | 71 | return true; 72 | } 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | io.github.magic-core 8 | excel-boot 9 | 2.0 10 | 11 | 12 | org.sonatype.oss 13 | oss-parent 14 | 9 15 | 16 | 17 | 18 | 19 | The Apache Software License, Version 2.0 20 | http://www.apache.org/licenses/LICENSE-2.0.txt 21 | repo 22 | 23 | 24 | 25 | 26 | https://github.com/programmeres/easy-poi 27 | https://github.com/programmeres/easy-poi.git 28 | https://github.com/programmeres/easy-poi 29 | 30 | 31 | 32 | NingWei 33 | magic_app@126.com 34 | https://github.com/programmeres/easy-poi 35 | 36 | 37 | 38 | 39 | 40 | 41 | org.apache.poi 42 | poi-ooxml 43 | 4.0.1 44 | 45 | 46 | org.apache.poi 47 | poi-ooxml-schemas 48 | 4.0.1 49 | 50 | 51 | 52 | xerces 53 | xercesImpl 54 | 2.12.0 55 | 56 | 57 | 58 | org.projectlombok 59 | lombok 60 | 1.18.4 61 | provided 62 | 63 | 64 | 65 | javax.servlet 66 | servlet-api 67 | 3.0-alpha-1 68 | provided 69 | 70 | 71 | 72 | org.slf4j 73 | slf4j-api 74 | 1.8.0-beta2 75 | 76 | 77 | 78 | com.google.guava 79 | guava 80 | 27.0.1-jre 81 | 82 | 83 | 84 | 85 | 86 | org.apache.maven.plugins 87 | maven-compiler-plugin 88 | 89 | 1.6 90 | 1.6 91 | UTF-8 92 | 93 | 94 | 95 | org.apache.maven.plugins 96 | maven-javadoc-plugin 97 | 2.9 98 | 99 | 100 | attach-javadocs 101 | 102 | jar 103 | 104 | 105 | -Xdoclint:none 106 | 107 | 108 | 109 | 110 | 111 | 112 | -------------------------------------------------------------------------------- /src/main/java/com/excel/poi/factory/ExcelMappingFactory.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 NingWei (ningww1@126.com) 3 | *

4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | *

16 | */ 17 | package com.excel.poi.factory; 18 | 19 | import com.excel.poi.annotation.ExportField; 20 | import com.excel.poi.annotation.ImportField; 21 | import com.excel.poi.entity.ExcelEntity; 22 | import com.excel.poi.entity.ExcelPropertyEntity; 23 | import com.excel.poi.exception.ExcelBootException; 24 | import java.lang.reflect.Field; 25 | import java.util.ArrayList; 26 | import java.util.List; 27 | 28 | /** 29 | * @author NingWei 30 | */ 31 | public class ExcelMappingFactory { 32 | 33 | /** 34 | * 根据指定Excel实体获取导入Excel文件相关信息 35 | * 36 | * @param clazz 37 | * @return 38 | * @throws IllegalAccessException 39 | * @throws InstantiationException 40 | */ 41 | public static ExcelEntity loadImportExcelClass(Class clazz) { 42 | List propertyList = new ArrayList(); 43 | 44 | Field[] fields = clazz.getDeclaredFields(); 45 | for (Field field : fields) { 46 | ImportField importField = field.getAnnotation(ImportField.class); 47 | if (null != importField) { 48 | field.setAccessible(true); 49 | ExcelPropertyEntity excelPropertyEntity = ExcelPropertyEntity.builder() 50 | .fieldEntity(field) 51 | .required(importField.required()) 52 | .dateFormat(importField.dateFormat().trim()) 53 | .regex(importField.regex().trim()) 54 | .regexMessage(importField.regexMessage().trim()) 55 | .scale(importField.scale()) 56 | .roundingMode(importField.roundingMode()) 57 | .build(); 58 | propertyList.add(excelPropertyEntity); 59 | } 60 | } 61 | if (propertyList.isEmpty()) { 62 | throw new ExcelBootException("[{}] 类未找到标注@ImportField注解的属性!", clazz.getName()); 63 | } 64 | ExcelEntity excelMapping = new ExcelEntity(); 65 | excelMapping.setPropertyList(propertyList); 66 | return excelMapping; 67 | 68 | } 69 | 70 | /** 71 | * 根据指定Excel实体获取导出Excel文件相关信息 72 | * 73 | * @param clazz 74 | * @return 75 | * @throws IllegalAccessException 76 | * @throws InstantiationException 77 | */ 78 | public static ExcelEntity loadExportExcelClass(Class clazz, String fileName) { 79 | List propertyList = new ArrayList(); 80 | 81 | Field[] fields = clazz.getDeclaredFields(); 82 | for (Field field : fields) { 83 | ExportField exportField = field.getAnnotation(ExportField.class); 84 | if (null != exportField) { 85 | field.setAccessible(true); 86 | ExcelPropertyEntity excelPropertyEntity = ExcelPropertyEntity.builder() 87 | .fieldEntity(field) 88 | .columnName(exportField.columnName().trim()) 89 | .scale(exportField.scale()) 90 | .roundingMode(exportField.roundingMode()) 91 | .dateFormat(exportField.dateFormat().trim()) 92 | .templateCellValue(exportField.defaultCellValue().trim()) 93 | .build(); 94 | propertyList.add(excelPropertyEntity); 95 | } 96 | } 97 | if (propertyList.isEmpty()) { 98 | throw new ExcelBootException("[{}]类未找到标注@ExportField注解的属性!", clazz.getName()); 99 | } 100 | ExcelEntity excelMapping = new ExcelEntity(); 101 | excelMapping.setPropertyList(propertyList); 102 | excelMapping.setFileName(fileName); 103 | return excelMapping; 104 | } 105 | 106 | 107 | } 108 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Artistic License 2.0 2 | Copyright (c) 3 | 4 | Everyone is permitted to copy and distribute verbatim copies of this license 5 | document, but changing it is not allowed. 6 | 7 | Preamble 8 | This license establishes the terms under which a given free software Package may 9 | be copied, modified, distributed, and/or redistributed. The intent is that the 10 | Copyright Holder maintains some artistic control over the development of that 11 | Package while still keeping the Package available as open source and free 12 | software. 13 | 14 | You are always permitted to make arrangements wholly outside of this license 15 | directly with the Copyright Holder of a given Package. If the terms of this 16 | license do not permit the full use that you propose to make of the Package, you 17 | should contact the Copyright Holder and seek a different licensing arrangement. 18 | 19 | Definitions 20 | "Copyright Holder" means the individual(s) or organization(s) named in the 21 | copyright notice for the entire Package. 22 | 23 | "Contributor" means any party that has contributed code or other material to the 24 | Package, in accordance with the Copyright Holder's procedures. 25 | 26 | "You" and "your" means any person who would like to copy, distribute, or modify 27 | the Package. 28 | 29 | "Package" means the collection of files distributed by the Copyright Holder, and 30 | derivatives of that collection and/or of those files. A given Package may 31 | consist of either the Standard Version, or a Modified Version. 32 | 33 | "Distribute" means providing a copy of the Package or making it accessible to 34 | anyone else, or in the case of a company or organization, to others outside of 35 | your company or organization. 36 | 37 | "Distributor Fee" means any fee that you charge for Distributing this Package or 38 | providing support for this Package to another party. It does not mean licensing 39 | fees. 40 | 41 | "Standard Version" refers to the Package if it has not been modified, or has 42 | been modified only in ways explicitly requested by the Copyright Holder. 43 | 44 | "Modified Version" means the Package, if it has been changed, and such changes 45 | were not explicitly requested by the Copyright Holder. 46 | 47 | "Original License" means this Artistic License as Distributed with the Standard 48 | Version of the Package, in its current version or as it may be modified by The 49 | Perl Foundation in the future. 50 | 51 | "Source" form means the source code, documentation source, and configuration 52 | files for the Package. 53 | 54 | "Compiled" form means the compiled bytecode, object code, binary, or any other 55 | form resulting from mechanical transformation or translation of the Source form. 56 | 57 | Permission for Use and Modification Without Distribution 58 | (1) You are permitted to use the Standard Version and create and use Modified 59 | Versions for any purpose without restriction, provided that you do not 60 | Distribute the Modified Version. 61 | 62 | Permissions for Redistribution of the Standard Version 63 | (2) You may Distribute verbatim copies of the Source form of the Standard 64 | Version of this Package in any medium without restriction, either gratis or for 65 | a Distributor Fee, provided that you duplicate all of the original copyright 66 | notices and associated disclaimers. At your discretion, such verbatim copies may 67 | or may not include a Compiled form of the Package. 68 | 69 | (3) You may apply any bug fixes, portability changes, and other modifications 70 | made available from the Copyright Holder. The resulting Package will still be 71 | considered the Standard Version, and as such will be subject to the Original 72 | License. 73 | 74 | Distribution of Modified Versions of the Package as Source 75 | (4) You may Distribute your Modified Version as Source (either gratis or for a 76 | Distributor Fee, and with or without a Compiled form of the Modified Version) 77 | provided that you clearly document how it differs from the Standard Version, 78 | including, but not limited to, documenting any non-standard features, 79 | executables, or modules, and provided that you do at least ONE of the following: 80 | 81 | (a) make the Modified Version available to the Copyright Holder of the Standard 82 | Version, under the Original License, so that the Copyright Holder may include 83 | your modifications in the Standard Version. 84 | (b) ensure that installation of your Modified Version does not prevent the user 85 | installing or running the Standard Version. In addition, the Modified Version 86 | must bear a name that is different from the name of the Standard Version. 87 | (c) allow anyone who receives a copy of the Modified Version to make the Source 88 | form of the Modified Version available to others under 89 | (i) the Original License or 90 | (ii) a license that permits the licensee to freely copy, modify and redistribute 91 | the Modified Version using the same licensing terms that apply to the copy that 92 | the licensee received, and requires that the Source form of the Modified 93 | Version, and of any works derived from it, be made freely available in that 94 | license fees are prohibited but Distributor Fees are allowed. 95 | 96 | Distribution of Compiled Forms of the Standard Version or Modified Versions 97 | without the Source 98 | (5) You may Distribute Compiled forms of the Standard Version without the 99 | Source, provided that you include complete instructions on how to get the Source 100 | of the Standard Version. Such instructions must be valid at the time of your 101 | distribution. If these instructions, at any time while you are carrying out such 102 | distribution, become invalid, you must provide new instructions on demand or 103 | cease further distribution. If you provide valid instructions or cease 104 | distribution within thirty days after you become aware that the instructions are 105 | invalid, then you do not forfeit any of your rights under this license. 106 | 107 | (6) You may Distribute a Modified Version in Compiled form without the Source, 108 | provided that you comply with Section 4 with respect to the Source of the 109 | Modified Version. 110 | 111 | Aggregating or Linking the Package 112 | (7) You may aggregate the Package (either the Standard Version or Modified 113 | Version) with other packages and Distribute the resulting aggregation provided 114 | that you do not charge a licensing fee for the Package. Distributor Fees are 115 | permitted, and licensing fees for other components in the aggregation are 116 | permitted. The terms of this license apply to the use and Distribution of the 117 | Standard or Modified Versions as included in the aggregation. 118 | 119 | (8) You are permitted to link Modified and Standard Versions with other works, 120 | to embed the Package in a larger work of your own, or to build stand-alone 121 | binary or bytecode versions of applications that include the Package, and 122 | Distribute the result without restriction, provided the result does not expose a 123 | direct interface to the Package. 124 | 125 | Items That are Not Considered Part of a Modified Version 126 | 127 | (9) Works (including, but not limited to, modules and scripts) that merely 128 | extend or make use of the Package, do not, by themselves, cause the Package to 129 | be a Modified Version. In addition, such works are not considered parts of the 130 | Package itself, and are not subject to the terms of this license. 131 | 132 | General Provisions 133 | 134 | (10) Any use, modification, and distribution of the Standard or Modified 135 | Versions is governed by this Artistic License. By using, modifying or 136 | distributing the Package, you accept this license. Do not use, modify, or 137 | distribute the Package, if you do not accept this license. 138 | 139 | (11) If your Modified Version has been derived from a Modified Version made by 140 | someone other than you, you are nevertheless required to ensure that your 141 | Modified Version complies with the requirements of this license. 142 | 143 | (12) This license does not grant you the right to use any trademark, service 144 | mark, tradename, or logo of the Copyright Holder. 145 | 146 | (13) This license includes the non-exclusive, worldwide, free-of-charge patent 147 | license to make, have made, use, offer to sell, sell, import and otherwise 148 | transfer the Package with respect to any patent claims licensable by the 149 | Copyright Holder that are necessarily infringed by the Package. If you institute 150 | patent litigation (including a cross-claim or counterclaim) against any party 151 | alleging that the Package constitutes direct or contributory patent 152 | infringement, then this Artistic License to you shall terminate on the date that 153 | such litigation is filed. 154 | 155 | (14) Disclaimer of Warranty: THE PACKAGE IS PROVIDED BY THE COPYRIGHT HOLDER AND 156 | CONTRIBUTORS "AS IS' AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES. THE IMPLIED 157 | WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR 158 | NON-INFRINGEMENT ARE DISCLAIMED TO THE EXTENT PERMITTED BY YOUR LOCAL LAW. 159 | UNLESS REQUIRED BY LAW, NO COPYRIGHT HOLDER OR CONTRIBUTOR WILL BE LIABLE FOR 160 | ANY DIRECT, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING IN ANY WAY 161 | OUT OF THE USE OF THE PACKAGE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH 162 | DAMAGE. -------------------------------------------------------------------------------- /src/main/java/com/excel/poi/excel/ExcelWriter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 NingWei (ningww1@126.com) 3 | *

4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | *

16 | */ 17 | /** 18 | * @author NingWei 19 | */ 20 | package com.excel.poi.excel; 21 | 22 | import static com.excel.poi.common.Constant.CHINESES_ATUO_SIZE_COLUMN_WIDTH_MAX; 23 | import static com.excel.poi.common.Constant.CHINESES_ATUO_SIZE_COLUMN_WIDTH_MIN; 24 | import static com.excel.poi.common.Constant.MAX_RECORD_COUNT_PEER_SHEET; 25 | import static com.excel.poi.common.DateFormatUtil.format; 26 | import com.excel.poi.common.StringUtil; 27 | import com.excel.poi.entity.ExcelEntity; 28 | import com.excel.poi.entity.ExcelPropertyEntity; 29 | import com.excel.poi.function.ExportFunction; 30 | import java.lang.reflect.Field; 31 | import java.lang.reflect.InvocationTargetException; 32 | import java.math.BigDecimal; 33 | import java.text.ParseException; 34 | import java.util.Date; 35 | import java.util.HashMap; 36 | import java.util.List; 37 | import java.util.Map; 38 | import lombok.extern.slf4j.Slf4j; 39 | import org.apache.poi.ss.usermodel.BorderStyle; 40 | import org.apache.poi.ss.usermodel.CellStyle; 41 | import org.apache.poi.ss.usermodel.FillPatternType; 42 | import org.apache.poi.ss.usermodel.Font; 43 | import org.apache.poi.ss.usermodel.HorizontalAlignment; 44 | import org.apache.poi.ss.usermodel.IndexedColors; 45 | import org.apache.poi.ss.usermodel.VerticalAlignment; 46 | import org.apache.poi.xssf.streaming.SXSSFCell; 47 | import org.apache.poi.xssf.streaming.SXSSFRow; 48 | import org.apache.poi.xssf.streaming.SXSSFSheet; 49 | import org.apache.poi.xssf.streaming.SXSSFWorkbook; 50 | import org.apache.poi.xssf.usermodel.XSSFCellStyle; 51 | import org.apache.poi.xssf.usermodel.XSSFColor; 52 | 53 | /** 54 | * 导出具体实现类 55 | * 56 | * @author NingWei 57 | */ 58 | @Slf4j 59 | public class ExcelWriter { 60 | 61 | private Integer rowAccessWindowSize; 62 | private ExcelEntity excelEntity; 63 | private Integer pageSize; 64 | private Integer nullCellCount = 0; 65 | private Integer recordCountPerSheet; 66 | private XSSFCellStyle headCellStyle; 67 | private Map columnWidthMap = new HashMap(); 68 | private Boolean openAutoColumWidth; 69 | 70 | public ExcelWriter(ExcelEntity excelEntity, Integer pageSize, Integer rowAccessWindowSize, Integer recordCountPerSheet, Boolean openAutoColumWidth) { 71 | this.excelEntity = excelEntity; 72 | this.pageSize = pageSize; 73 | this.rowAccessWindowSize = rowAccessWindowSize; 74 | this.recordCountPerSheet = recordCountPerSheet; 75 | this.openAutoColumWidth = openAutoColumWidth; 76 | } 77 | 78 | /** 79 | * @param param 80 | * @param exportFunction 81 | * @param

82 | * @param 83 | * @return 84 | * @throws InvocationTargetException 85 | * @throws NoSuchMethodException 86 | * @throws ParseException 87 | * @throws IllegalAccessException 88 | */ 89 | public SXSSFWorkbook generateWorkbook(P param, ExportFunction exportFunction) throws Exception { 90 | SXSSFWorkbook workbook = new SXSSFWorkbook(rowAccessWindowSize); 91 | int sheetNo = 1; 92 | int rowNum = 1; 93 | List propertyList = excelEntity.getPropertyList(); 94 | //初始化第一行 95 | SXSSFSheet sheet = generateHeader(workbook, propertyList, excelEntity.getFileName()); 96 | 97 | //生成其他行 98 | int firstPageNo = 1; 99 | while (true) { 100 | List data = exportFunction.pageQuery(param, firstPageNo, pageSize); 101 | if (data == null || data.isEmpty()) { 102 | if (rowNum != 1) { 103 | sizeColumWidth(sheet, propertyList.size()); 104 | } 105 | log.warn("查询结果为空,结束查询!"); 106 | break; 107 | } 108 | int dataSize = data.size(); 109 | for (int i = 1; i <= dataSize; i++, rowNum++) { 110 | T queryResult = data.get(i - 1); 111 | Object convertResult = exportFunction.convert(queryResult); 112 | if (rowNum > MAX_RECORD_COUNT_PEER_SHEET) { 113 | sizeColumWidth(sheet, propertyList.size()); 114 | sheet = generateHeader(workbook, propertyList, excelEntity.getFileName() + "_" + sheetNo); 115 | sheetNo++; 116 | rowNum = 1; 117 | columnWidthMap.clear(); 118 | } 119 | SXSSFRow row = sheet.createRow(rowNum); 120 | for (int j = 0; j < propertyList.size(); j++) { 121 | SXSSFCell cell = row.createCell(j); 122 | buildCellValue(cell, convertResult, propertyList.get(j)); 123 | calculateColumWidth(cell, j); 124 | } 125 | if (nullCellCount == propertyList.size()) { 126 | log.warn("忽略一行空数据!"); 127 | sheet.removeRow(row); 128 | rowNum--; 129 | } 130 | nullCellCount = 0; 131 | 132 | } 133 | if (data.size() < pageSize) { 134 | sizeColumWidth(sheet, propertyList.size()); 135 | log.warn("查询结果数量小于pageSize,结束查询!"); 136 | break; 137 | } 138 | firstPageNo++; 139 | } 140 | return workbook; 141 | } 142 | 143 | 144 | /** 145 | * 构建模板Excel 146 | * 147 | * @param 148 | * @param 149 | * @return 150 | */ 151 | public SXSSFWorkbook generateTemplateWorkbook() { 152 | SXSSFWorkbook workbook = new SXSSFWorkbook(rowAccessWindowSize); 153 | 154 | List propertyList = excelEntity.getPropertyList(); 155 | SXSSFSheet sheet = generateHeader(workbook, propertyList, excelEntity.getFileName()); 156 | 157 | SXSSFRow row = sheet.createRow(1); 158 | for (int j = 0; j < propertyList.size(); j++) { 159 | SXSSFCell cell = row.createCell(j); 160 | cell.setCellValue(propertyList.get(j).getTemplateCellValue()); 161 | calculateColumWidth(cell, j); 162 | } 163 | sizeColumWidth(sheet, propertyList.size()); 164 | return workbook; 165 | } 166 | 167 | /** 168 | * 构建多Sheet Excel 169 | * 170 | * @param param 171 | * @param exportFunction 172 | * @param 173 | * @param 174 | * @return 175 | * @throws InvocationTargetException 176 | * @throws NoSuchMethodException 177 | * @throws ParseException 178 | * @throws IllegalAccessException 179 | */ 180 | public SXSSFWorkbook generateMultiSheetWorkbook(R param, ExportFunction exportFunction) throws Exception { 181 | int pageNo = 1; 182 | int sheetNo = 1; 183 | int rowNum = 1; 184 | SXSSFWorkbook workbook = new SXSSFWorkbook(rowAccessWindowSize); 185 | List propertyList = excelEntity.getPropertyList(); 186 | SXSSFSheet sheet = generateHeader(workbook, propertyList, excelEntity.getFileName()); 187 | 188 | while (true) { 189 | List data = exportFunction.pageQuery(param, pageNo, pageSize); 190 | if (data == null || data.isEmpty()) { 191 | if (rowNum != 1) { 192 | sizeColumWidth(sheet, propertyList.size()); 193 | } 194 | log.warn("查询结果为空,结束查询!"); 195 | break; 196 | } 197 | for (int i = 1; i <= data.size(); i++, rowNum++) { 198 | T queryResult = data.get(i - 1); 199 | Object convertResult = exportFunction.convert(queryResult); 200 | if (rowNum > recordCountPerSheet) { 201 | sizeColumWidth(sheet, propertyList.size()); 202 | sheet = generateHeader(workbook, propertyList, excelEntity.getFileName() + "_" + sheetNo); 203 | sheetNo++; 204 | rowNum = 1; 205 | columnWidthMap.clear(); 206 | } 207 | SXSSFRow bodyRow = sheet.createRow(rowNum); 208 | for (int j = 0; j < propertyList.size(); j++) { 209 | SXSSFCell cell = bodyRow.createCell(j); 210 | buildCellValue(cell, convertResult, propertyList.get(j)); 211 | calculateColumWidth(cell, j); 212 | } 213 | if (nullCellCount == propertyList.size()) { 214 | log.warn("忽略一行空数据!"); 215 | sheet.removeRow(bodyRow); 216 | rowNum--; 217 | } 218 | nullCellCount = 0; 219 | } 220 | if (data.size() < pageSize) { 221 | sizeColumWidth(sheet, propertyList.size()); 222 | log.warn("查询结果数量小于pageSize,结束查询!"); 223 | break; 224 | } 225 | pageNo++; 226 | } 227 | return workbook; 228 | } 229 | 230 | /** 231 | * 自动适配中文单元格 232 | * 233 | * @param sheet 234 | * @param cell 235 | * @param columnIndex 236 | */ 237 | private void sizeColumWidth(SXSSFSheet sheet, Integer columnSize) { 238 | if (openAutoColumWidth) { 239 | for (int j = 0; j < columnSize; j++) { 240 | if (columnWidthMap.get(j) != null) { 241 | sheet.setColumnWidth(j, columnWidthMap.get(j) * 256); 242 | } 243 | } 244 | } 245 | } 246 | 247 | /** 248 | * 自动适配中文单元格 249 | * 250 | * @param sheet 251 | * @param cell 252 | * @param columnIndex 253 | */ 254 | private void calculateColumWidth(SXSSFCell cell, Integer columnIndex) { 255 | if (openAutoColumWidth) { 256 | String cellValue = cell.getStringCellValue(); 257 | int length = cellValue.getBytes().length; 258 | length += (int) Math.ceil((double) ((cellValue.length() * 3 - length) / 2) * 0.1D); 259 | length = Math.max(length, CHINESES_ATUO_SIZE_COLUMN_WIDTH_MIN); 260 | length = Math.min(length, CHINESES_ATUO_SIZE_COLUMN_WIDTH_MAX); 261 | if (columnWidthMap.get(columnIndex) == null || columnWidthMap.get(columnIndex) < length) { 262 | columnWidthMap.put(columnIndex, length); 263 | } 264 | } 265 | } 266 | 267 | /** 268 | * 初始化第一行的属性 269 | * 270 | * @param workbook 271 | * @param propertyList 272 | * @param sheetName 273 | * @return 274 | */ 275 | private SXSSFSheet generateHeader(SXSSFWorkbook workbook, List propertyList, String sheetName) { 276 | SXSSFSheet sheet = workbook.createSheet(sheetName); 277 | SXSSFRow headerRow = sheet.createRow(0); 278 | headerRow.setHeight((short) 600); 279 | CellStyle headCellStyle = getHeaderCellStyle(workbook); 280 | for (int i = 0; i < propertyList.size(); i++) { 281 | SXSSFCell cell = headerRow.createCell(i); 282 | cell.setCellStyle(headCellStyle); 283 | cell.setCellValue(propertyList.get(i).getColumnName()); 284 | calculateColumWidth(cell, i); 285 | } 286 | return sheet; 287 | } 288 | 289 | /** 290 | * 构造 除第一行以外的其他行的列值 291 | * 292 | * @param cell 293 | * @param entity 294 | * @param property 295 | */ 296 | private void buildCellValue(SXSSFCell cell, Object entity, ExcelPropertyEntity property) throws Exception { 297 | Field field = property.getFieldEntity(); 298 | Object cellValue = field.get(entity); 299 | if (StringUtil.isBlank(cellValue) || "0".equals(cellValue.toString()) || "0.0".equals(cellValue.toString()) || "0.00".equals(cellValue.toString())) { 300 | nullCellCount++; 301 | } 302 | if (cellValue == null) { 303 | cell.setCellValue(""); 304 | } else if (cellValue instanceof BigDecimal) { 305 | if (-1 == property.getScale()) { 306 | cell.setCellValue(cellValue.toString()); 307 | } else { 308 | cell.setCellValue((((BigDecimal) cellValue).setScale(property.getScale(), property.getRoundingMode())).toString()); 309 | } 310 | } else if (cellValue instanceof Date) { 311 | cell.setCellValue(format(property.getDateFormat(), (Date) cellValue)); 312 | } else { 313 | cell.setCellValue(cellValue.toString()); 314 | } 315 | } 316 | 317 | 318 | public CellStyle getHeaderCellStyle(SXSSFWorkbook workbook) { 319 | if (headCellStyle == null) { 320 | headCellStyle = workbook.getXSSFWorkbook().createCellStyle(); 321 | headCellStyle.setBorderTop(BorderStyle.NONE); 322 | headCellStyle.setBorderRight(BorderStyle.NONE); 323 | headCellStyle.setBorderBottom(BorderStyle.NONE); 324 | headCellStyle.setBorderLeft(BorderStyle.NONE); 325 | headCellStyle.setAlignment(HorizontalAlignment.CENTER); 326 | headCellStyle.setVerticalAlignment(VerticalAlignment.CENTER); 327 | XSSFColor color = new XSSFColor(new java.awt.Color(217, 217, 217)); 328 | headCellStyle.setFillForegroundColor(color); 329 | headCellStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND); 330 | Font font = workbook.createFont(); 331 | font.setFontName("微软雅黑"); 332 | font.setColor(IndexedColors.ROYAL_BLUE.index); 333 | font.setBold(true); 334 | headCellStyle.setFont(font); 335 | headCellStyle.setDataFormat(workbook.createDataFormat().getFormat("@")); 336 | } 337 | return headCellStyle; 338 | } 339 | } 340 | -------------------------------------------------------------------------------- /src/main/java/com/excel/poi/ExcelBoot.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 NingWei (ningww1@126.com) 3 | *

4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | *

16 | */ 17 | package com.excel.poi; 18 | 19 | import com.excel.poi.common.Constant; 20 | import com.excel.poi.entity.ExcelEntity; 21 | import com.excel.poi.excel.ExcelReader; 22 | import com.excel.poi.excel.ExcelWriter; 23 | import com.excel.poi.exception.ExcelBootException; 24 | import com.excel.poi.factory.ExcelMappingFactory; 25 | import com.excel.poi.function.ExportFunction; 26 | import com.excel.poi.function.ImportFunction; 27 | import java.io.IOException; 28 | import java.io.InputStream; 29 | import java.io.OutputStream; 30 | import java.net.URLEncoder; 31 | import javax.servlet.http.HttpServletResponse; 32 | import lombok.extern.slf4j.Slf4j; 33 | import org.apache.poi.openxml4j.exceptions.OpenXML4JException; 34 | import org.apache.poi.xssf.streaming.SXSSFWorkbook; 35 | import org.xml.sax.SAXException; 36 | 37 | /** 38 | * @author NingWei 39 | */ 40 | @Slf4j 41 | public class ExcelBoot { 42 | private HttpServletResponse httpServletResponse; 43 | private OutputStream outputStream; 44 | private InputStream inputStream; 45 | private String fileName; 46 | private Class excelClass; 47 | private Integer pageSize; 48 | private Integer rowAccessWindowSize; 49 | private Integer recordCountPerSheet; 50 | private Boolean openAutoColumWidth; 51 | 52 | /** 53 | * 导入构造器 54 | * 55 | * @param inputStream 56 | * @param excelClass 57 | */ 58 | protected ExcelBoot(InputStream inputStream, Class excelClass) { 59 | this(null, null, inputStream, null, excelClass, null, null, null, null); 60 | } 61 | 62 | /** 63 | * OutputStream导出构造器,一般用于导出到ftp服务器 64 | * 65 | * @param outputStream 66 | * @param fileName 67 | * @param excelClass 68 | */ 69 | protected ExcelBoot(OutputStream outputStream, String fileName, Class excelClass) { 70 | this(null, outputStream, null, fileName, excelClass, Constant.DEFAULT_PAGE_SIZE, Constant.DEFAULT_ROW_ACCESS_WINDOW_SIZE, Constant.DEFAULT_RECORD_COUNT_PEER_SHEET, Constant.OPEN_AUTO_COLUM_WIDTH); 71 | } 72 | 73 | /** 74 | * HttpServletResponse导出构造器,一般用于浏览器导出 75 | * 76 | * @param response 77 | * @param fileName 78 | * @param excelClass 79 | */ 80 | protected ExcelBoot(HttpServletResponse response, String fileName, Class excelClass) { 81 | this(response, null, null, fileName, excelClass, Constant.DEFAULT_PAGE_SIZE, Constant.DEFAULT_ROW_ACCESS_WINDOW_SIZE, Constant.DEFAULT_RECORD_COUNT_PEER_SHEET, Constant.OPEN_AUTO_COLUM_WIDTH); 82 | } 83 | 84 | /** 85 | * 构造器 86 | * 87 | * @param response 88 | * @param outputStream 89 | * @param inputStream 90 | * @param fileName 91 | * @param excelClass 92 | * @param pageSize 93 | * @param rowAccessWindowSize 94 | * @param recordCountPerSheet 95 | * @param openAutoColumWidth 96 | */ 97 | protected ExcelBoot(HttpServletResponse response, OutputStream outputStream, InputStream inputStream 98 | , String fileName, Class excelClass, Integer pageSize, Integer rowAccessWindowSize, Integer recordCountPerSheet, Boolean openAutoColumWidth) { 99 | this.httpServletResponse = response; 100 | this.outputStream = outputStream; 101 | this.inputStream = inputStream; 102 | this.fileName = fileName; 103 | this.excelClass = excelClass; 104 | this.pageSize = pageSize; 105 | this.rowAccessWindowSize = rowAccessWindowSize; 106 | this.recordCountPerSheet = recordCountPerSheet; 107 | this.openAutoColumWidth = openAutoColumWidth; 108 | } 109 | 110 | /** 111 | * 通过HttpServletResponse,一般用于在浏览器中导出excel 112 | * 113 | * @param httpServletResponse 114 | * @param fileName 115 | * @param clazz 116 | * @return 117 | */ 118 | public static ExcelBoot ExportBuilder(HttpServletResponse httpServletResponse, String fileName, Class clazz) { 119 | return new ExcelBoot(httpServletResponse, fileName, clazz); 120 | } 121 | 122 | /** 123 | * 通过OutputStream生成excel文件,一般用于异步导出大Excel文件到ftp服务器或本地路径 124 | * 125 | * @param outputStream 126 | * @param fileName 127 | * @param clazz 128 | * @return 129 | */ 130 | public static ExcelBoot ExportBuilder(OutputStream outputStream, String fileName, Class clazz) { 131 | return new ExcelBoot(outputStream, fileName, clazz); 132 | } 133 | 134 | /** 135 | * HttpServletResponse 通用导出Excel构造器 136 | * 137 | * @param response 138 | * @param fileName 139 | * @param excelClass 140 | * @param pageSize 141 | * @param rowAccessWindowSize 142 | * @param recordCountPerSheet 143 | * @param openAutoColumWidth 144 | * @return 145 | */ 146 | public static ExcelBoot ExportBuilder(HttpServletResponse response, String fileName, Class excelClass, 147 | Integer pageSize, Integer rowAccessWindowSize, Integer recordCountPerSheet, Boolean openAutoColumWidth) { 148 | return new ExcelBoot(response, null, null 149 | , fileName, excelClass, pageSize, rowAccessWindowSize, recordCountPerSheet, openAutoColumWidth); 150 | } 151 | 152 | /** 153 | * OutputStream 通用导出Excel构造器 154 | * 155 | * @param outputStream 156 | * @param fileName 157 | * @param excelClass 158 | * @param pageSize 159 | * @param rowAccessWindowSize 160 | * @param recordCountPerSheet 161 | * @param openAutoColumWidth 162 | * @return 163 | */ 164 | public static ExcelBoot ExportBuilder(OutputStream outputStream, String fileName, Class excelClass, Integer pageSize 165 | , Integer rowAccessWindowSize, Integer recordCountPerSheet, Boolean openAutoColumWidth) { 166 | return new ExcelBoot(null, outputStream, null 167 | , fileName, excelClass, pageSize, rowAccessWindowSize, recordCountPerSheet, openAutoColumWidth); 168 | } 169 | 170 | /** 171 | * 导入Excel文件数据 172 | * 173 | * @param inputStreamm 174 | * @param clazz 175 | * @return 176 | */ 177 | public static ExcelBoot ImportBuilder(InputStream inputStreamm, Class clazz) { 178 | return new ExcelBoot(inputStreamm, clazz); 179 | } 180 | 181 | /** 182 | * 用于浏览器导出 183 | * 184 | * @param param 185 | * @param exportFunction 186 | * @param ExportFunction 187 | * @param 188 | * @param 189 | */ 190 | public void exportResponse(R param, ExportFunction exportFunction) { 191 | SXSSFWorkbook sxssfWorkbook = null; 192 | try { 193 | try { 194 | verifyResponse(); 195 | sxssfWorkbook = commonSingleSheet(param, exportFunction); 196 | download(sxssfWorkbook, httpServletResponse, URLEncoder.encode(fileName + ".xlsx", "UTF-8")); 197 | } finally { 198 | if (sxssfWorkbook != null) { 199 | sxssfWorkbook.close(); 200 | } 201 | if (httpServletResponse != null && httpServletResponse.getOutputStream() != null) { 202 | httpServletResponse.getOutputStream().close(); 203 | } 204 | } 205 | } catch (Exception e) { 206 | throw new ExcelBootException(e); 207 | } 208 | } 209 | 210 | /** 211 | * 通过OutputStream导出excel文件,一般用于异步导出大Excel文件到本地路径 212 | * 213 | * @param param 214 | * @param ExportFunction 215 | * @param exportFunction 216 | * @param 217 | * @param 218 | */ 219 | public void exportStream(R param, ExportFunction exportFunction) { 220 | OutputStream outputStream = null; 221 | try { 222 | try { 223 | outputStream = generateStream(param, exportFunction); 224 | write(outputStream); 225 | } finally { 226 | if (outputStream != null) { 227 | outputStream.close(); 228 | } 229 | } 230 | } catch (Exception e) { 231 | throw new ExcelBootException(e); 232 | } 233 | } 234 | 235 | /** 236 | * 通过OutputStream导出excel文件,一般用于异步导出大Excel文件到ftp服务器 237 | * 238 | * @param param 239 | * @param exportFunction 240 | * @param ExportFunction 241 | * @param 242 | * @param 243 | * @return 244 | */ 245 | public OutputStream generateStream(R param, ExportFunction exportFunction) throws IOException { 246 | SXSSFWorkbook sxssfWorkbook = null; 247 | try { 248 | verifyStream(); 249 | sxssfWorkbook = commonSingleSheet(param, exportFunction); 250 | sxssfWorkbook.write(outputStream); 251 | return outputStream; 252 | } catch (Exception e) { 253 | log.error("生成Excel发生异常! 异常信息:", e); 254 | if (sxssfWorkbook != null) { 255 | sxssfWorkbook.close(); 256 | } 257 | throw new ExcelBootException(e); 258 | } 259 | } 260 | 261 | /** 262 | * 用于浏览器分sheet导出 263 | * 264 | * @param param 265 | * @param exportFunction 266 | * @param ExportFunction 267 | * @param 268 | * @param 269 | */ 270 | public void exportMultiSheetResponse(R param, ExportFunction exportFunction) { 271 | SXSSFWorkbook sxssfWorkbook = null; 272 | try { 273 | try { 274 | verifyResponse(); 275 | sxssfWorkbook = commonMultiSheet(param, exportFunction); 276 | download(sxssfWorkbook, httpServletResponse, URLEncoder.encode(fileName + ".xlsx", "UTF-8")); 277 | } finally { 278 | if (sxssfWorkbook != null) { 279 | sxssfWorkbook.close(); 280 | } 281 | } 282 | } catch (Exception e) { 283 | throw new ExcelBootException(e); 284 | } 285 | } 286 | 287 | 288 | /** 289 | * 通过OutputStream分sheet导出excel文件,一般用于异步导出大Excel文件到本地路径 290 | * 291 | * @param param 292 | * @param ExportFunction 293 | * @param exportFunction 294 | * @param 295 | * @param 296 | */ 297 | public void exportMultiSheetStream(R param, ExportFunction exportFunction) { 298 | OutputStream outputStream = null; 299 | try { 300 | try { 301 | outputStream = generateMultiSheetStream(param, exportFunction); 302 | write(outputStream); 303 | } finally { 304 | if (outputStream != null) { 305 | outputStream.close(); 306 | } 307 | } 308 | } catch (Exception e) { 309 | throw new ExcelBootException(e); 310 | } 311 | } 312 | 313 | 314 | /** 315 | * 通过OutputStream分sheet导出excel文件,一般用于异步导出大Excel文件到ftp服务器 316 | * 317 | * @param param 318 | * @param exportFunction 319 | * @param ExportFunction 320 | * @param 321 | * @param 322 | * @return 323 | */ 324 | public OutputStream generateMultiSheetStream(R param, ExportFunction exportFunction) throws IOException { 325 | SXSSFWorkbook sxssfWorkbook = null; 326 | try { 327 | verifyStream(); 328 | sxssfWorkbook = commonMultiSheet(param, exportFunction); 329 | sxssfWorkbook.write(outputStream); 330 | return outputStream; 331 | } catch (Exception e) { 332 | log.error("分Sheet生成Excel发生异常! 异常信息:", e); 333 | if (sxssfWorkbook != null) { 334 | sxssfWorkbook.close(); 335 | } 336 | throw new ExcelBootException(e); 337 | } 338 | } 339 | 340 | /** 341 | * 导出-导入模板 342 | * 343 | * @param data 344 | * @throws Exception 345 | */ 346 | public void exportTemplate() { 347 | SXSSFWorkbook sxssfWorkbook = null; 348 | try { 349 | try { 350 | verifyResponse(); 351 | verifyParams(); 352 | ExcelEntity excelMapping = ExcelMappingFactory.loadExportExcelClass(excelClass, fileName); 353 | ExcelWriter excelWriter = new ExcelWriter(excelMapping, pageSize, rowAccessWindowSize, recordCountPerSheet, openAutoColumWidth); 354 | sxssfWorkbook = excelWriter.generateTemplateWorkbook(); 355 | download(sxssfWorkbook, httpServletResponse, URLEncoder.encode(fileName + ".xlsx", "UTF-8")); 356 | } finally { 357 | if (sxssfWorkbook != null) { 358 | sxssfWorkbook.close(); 359 | } 360 | if (httpServletResponse != null && httpServletResponse.getOutputStream() != null) { 361 | httpServletResponse.getOutputStream().close(); 362 | } 363 | } 364 | } catch (Exception e) { 365 | throw new ExcelBootException(e); 366 | } 367 | } 368 | 369 | /** 370 | * 导入excel全部sheet 371 | * 372 | * @param inputStream 373 | * @param importFunction 374 | * @throws OpenXML4JException 375 | * @throws SAXException 376 | * @throws IOException 377 | */ 378 | public void importExcel(ImportFunction importFunction) { 379 | try { 380 | if (importFunction == null) { 381 | throw new ExcelBootException("excelReadHandler参数为空!"); 382 | } 383 | if (inputStream == null) { 384 | throw new ExcelBootException("inputStream参数为空!"); 385 | } 386 | 387 | ExcelEntity excelMapping = ExcelMappingFactory.loadImportExcelClass(excelClass); 388 | ExcelReader excelReader = new ExcelReader(excelClass, excelMapping, importFunction); 389 | excelReader.process(inputStream); 390 | } catch (Exception e) { 391 | throw new ExcelBootException(e); 392 | } 393 | 394 | } 395 | 396 | private SXSSFWorkbook commonSingleSheet(R param, ExportFunction exportFunction) throws Exception { 397 | verifyParams(); 398 | ExcelEntity excelMapping = ExcelMappingFactory.loadExportExcelClass(excelClass, fileName); 399 | ExcelWriter excelWriter = new ExcelWriter(excelMapping, pageSize, rowAccessWindowSize, recordCountPerSheet, openAutoColumWidth); 400 | return excelWriter.generateWorkbook(param, exportFunction); 401 | } 402 | 403 | private SXSSFWorkbook commonMultiSheet(R param, ExportFunction exportFunction) throws Exception { 404 | verifyParams(); 405 | ExcelEntity excelMapping = ExcelMappingFactory.loadExportExcelClass(excelClass, fileName); 406 | ExcelWriter excelWriter = new ExcelWriter(excelMapping, pageSize, rowAccessWindowSize, recordCountPerSheet, openAutoColumWidth); 407 | return excelWriter.generateMultiSheetWorkbook(param, exportFunction); 408 | } 409 | 410 | /** 411 | * 生成文件 412 | * 413 | * @param out 414 | * @throws IOException 415 | */ 416 | private void write(OutputStream out) throws IOException { 417 | if (null != out) { 418 | out.flush(); 419 | } 420 | } 421 | 422 | 423 | /** 424 | * 构建Excel服务器响应格式 425 | * 426 | * @param wb 427 | * @param response 428 | * @param filename 429 | * @throws IOException 430 | */ 431 | private void download(SXSSFWorkbook wb, HttpServletResponse response, String filename) throws IOException { 432 | OutputStream out = response.getOutputStream(); 433 | response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"); 434 | response.setHeader("Content-disposition", 435 | String.format("attachment; filename=%s", filename)); 436 | if (null != out) { 437 | wb.write(out); 438 | out.flush(); 439 | } 440 | } 441 | 442 | private void verifyResponse() { 443 | if (httpServletResponse == null) { 444 | throw new ExcelBootException("httpServletResponse参数为空!"); 445 | } 446 | } 447 | 448 | private void verifyStream() { 449 | if (outputStream == null) { 450 | throw new ExcelBootException("outputStream参数为空!"); 451 | } 452 | } 453 | 454 | private void verifyParams() { 455 | if (excelClass == null) { 456 | throw new ExcelBootException("excelClass参数为空!"); 457 | } 458 | if (fileName == null) { 459 | throw new ExcelBootException("fileName参数为空!"); 460 | } 461 | } 462 | 463 | } -------------------------------------------------------------------------------- /src/main/java/com/excel/poi/excel/ExcelReader.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 NingWei (ningww1@126.com) 3 | *

4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | *

16 | */ 17 | package com.excel.poi.excel; 18 | 19 | import com.excel.poi.common.Constant; 20 | import static com.excel.poi.common.DateFormatUtil.parse; 21 | import static com.excel.poi.common.RegexUtil.isMatch; 22 | import com.excel.poi.common.StringUtil; 23 | import static com.excel.poi.common.StringUtil.convertNullTOZERO; 24 | import com.excel.poi.entity.ErrorEntity; 25 | import com.excel.poi.entity.ExcelEntity; 26 | import com.excel.poi.entity.ExcelPropertyEntity; 27 | import com.excel.poi.exception.AllEmptyRowException; 28 | import com.excel.poi.exception.ExcelBootException; 29 | import com.excel.poi.function.ImportFunction; 30 | import java.io.IOException; 31 | import java.io.InputStream; 32 | import java.lang.reflect.Field; 33 | import java.math.BigDecimal; 34 | import java.text.ParseException; 35 | import java.util.ArrayList; 36 | import java.util.Date; 37 | import java.util.Iterator; 38 | import java.util.List; 39 | import java.util.concurrent.ExecutionException; 40 | import lombok.extern.slf4j.Slf4j; 41 | import org.apache.poi.openxml4j.exceptions.OpenXML4JException; 42 | import org.apache.poi.openxml4j.opc.OPCPackage; 43 | import org.apache.poi.xssf.eventusermodel.XSSFReader; 44 | import org.apache.poi.xssf.model.SharedStringsTable; 45 | import org.apache.poi.xssf.usermodel.XSSFRichTextString; 46 | import org.xml.sax.Attributes; 47 | import org.xml.sax.InputSource; 48 | import org.xml.sax.SAXException; 49 | import org.xml.sax.XMLReader; 50 | import org.xml.sax.helpers.DefaultHandler; 51 | import org.xml.sax.helpers.XMLReaderFactory; 52 | 53 | /** 54 | * @author NingWei 55 | */ 56 | @Slf4j 57 | public class ExcelReader extends DefaultHandler { 58 | private Integer currentSheetIndex = -1; 59 | private Integer currentRowIndex = 0; 60 | private Integer excelCurrentCellIndex = 0; 61 | private ExcelCellType cellFormatStr; 62 | private String currentCellLocation; 63 | private String previousCellLocation; 64 | private String endCellLocation; 65 | private SharedStringsTable mSharedStringsTable; 66 | private String currentCellValue; 67 | private Boolean isNeedSharedStrings = false; 68 | private ExcelEntity excelMapping; 69 | private ImportFunction importFunction; 70 | private Class excelClass; 71 | private List cellsOnRow = new ArrayList(); 72 | private Integer beginReadRowIndex; 73 | private Integer dataCurrentCellIndex = -1; 74 | 75 | 76 | public ExcelReader(Class entityClass, 77 | ExcelEntity excelMapping, 78 | ImportFunction importFunction) { 79 | this(entityClass, excelMapping, 1, importFunction); 80 | } 81 | 82 | public ExcelReader(Class entityClass, 83 | ExcelEntity excelMapping, 84 | Integer beginReadRowIndex, 85 | ImportFunction importFunction) { 86 | this.excelClass = entityClass; 87 | this.excelMapping = excelMapping; 88 | this.beginReadRowIndex = beginReadRowIndex; 89 | this.importFunction = importFunction; 90 | } 91 | 92 | public void process(InputStream in) 93 | throws IOException, OpenXML4JException, SAXException { 94 | OPCPackage opcPackage = null; 95 | InputStream sheet = null; 96 | InputSource sheetSource; 97 | try { 98 | opcPackage = OPCPackage.open(in); 99 | XSSFReader xssfReader = new XSSFReader(opcPackage); 100 | XMLReader parser = this.fetchSheetParser(xssfReader.getSharedStringsTable()); 101 | 102 | Iterator sheets = xssfReader.getSheetsData(); 103 | while (sheets.hasNext()) { 104 | currentRowIndex = 0; 105 | currentSheetIndex++; 106 | try { 107 | sheet = sheets.next(); 108 | sheetSource = new InputSource(sheet); 109 | try { 110 | log.info("开始读取第{}个Sheet!", currentSheetIndex + 1); 111 | parser.parse(sheetSource); 112 | } catch (AllEmptyRowException e) { 113 | log.warn(e.getMessage()); 114 | } catch (Exception e) { 115 | throw new ExcelBootException(e, "第{}个Sheet,第{}行,第{}列,系统发生异常! ", currentSheetIndex + 1, currentRowIndex + 1, dataCurrentCellIndex + 1); 116 | } 117 | } finally { 118 | if (sheet != null) { 119 | sheet.close(); 120 | } 121 | } 122 | } 123 | } finally { 124 | if (opcPackage != null) { 125 | opcPackage.close(); 126 | } 127 | } 128 | } 129 | 130 | /** 131 | * 获取sharedStrings.xml文件的XMLReader对象 132 | * 133 | * @param sst 134 | * @return 135 | * @throws SAXException 136 | */ 137 | private XMLReader fetchSheetParser(SharedStringsTable sst) throws SAXException { 138 | XMLReader parser = XMLReaderFactory.createXMLReader("org.apache.xerces.parsers.SAXParser"); 139 | this.mSharedStringsTable = sst; 140 | parser.setContentHandler(this); 141 | return parser; 142 | } 143 | 144 | /** 145 | * 开始读取一个标签元素 146 | * 147 | * @param uri 148 | * @param localName 149 | * @param name 150 | * @param attributes 151 | */ 152 | @Override 153 | public void startElement(String uri, String localName, String name, Attributes attributes) { 154 | if (Constant.CELL.equals(name)) { 155 | String xyzLocation = attributes.getValue(Constant.XYZ_LOCATION); 156 | previousCellLocation = null == previousCellLocation ? xyzLocation : currentCellLocation; 157 | currentCellLocation = xyzLocation; 158 | String cellType = attributes.getValue(Constant.CELL_T_PROPERTY); 159 | isNeedSharedStrings = (null != cellType && cellType.equals(Constant.CELL_S_VALUE)); 160 | setCellType(cellType); 161 | } 162 | currentCellValue = ""; 163 | } 164 | 165 | 166 | /** 167 | * 加载v标签中间的值 168 | * 169 | * @param chars 170 | * @param start 171 | * @param length 172 | */ 173 | @Override 174 | public void characters(char[] chars, int start, int length) { 175 | currentCellValue = currentCellValue.concat(new String(chars, start, length)); 176 | } 177 | 178 | /** 179 | * 结束读取一个标签元素 180 | * 181 | * @param uri 182 | * @param localName 183 | * @param name 184 | * @throws SAXException 185 | */ 186 | @Override 187 | public void endElement(String uri, String localName, String name) { 188 | if (Constant.CELL.equals(name)) { 189 | if (isNeedSharedStrings && !StringUtil.isBlank(currentCellValue) && StringUtil.isNumeric(currentCellValue)) { 190 | int index = Integer.parseInt(currentCellValue); 191 | currentCellValue = new XSSFRichTextString(mSharedStringsTable.getEntryAt(index)).toString(); 192 | } 193 | if (!currentCellLocation.equals(previousCellLocation) && currentRowIndex != 0) { 194 | for (int i = 0; i < countNullCell(currentCellLocation, previousCellLocation); i++) { 195 | cellsOnRow.add(excelCurrentCellIndex, ""); 196 | excelCurrentCellIndex++; 197 | } 198 | } 199 | if (currentRowIndex != 0 || !"".equals(currentCellValue.trim())) { 200 | String value = this.getCellValue(currentCellValue.trim()); 201 | cellsOnRow.add(excelCurrentCellIndex, value); 202 | excelCurrentCellIndex++; 203 | } 204 | } 205 | else if (Constant.ROW.equals(name)) { 206 | if (currentRowIndex == 0) { 207 | endCellLocation = currentCellLocation; 208 | int propertySize = excelMapping.getPropertyList().size(); 209 | if (cellsOnRow.size() != propertySize) { 210 | throw new ExcelBootException("Excel有效列数不等于标注注解的属性数量!Excel列数:{},标注注解的属性数量:{}", cellsOnRow.size(), propertySize); 211 | } 212 | } 213 | if (null != endCellLocation) { 214 | for (int i = 0; i <= countNullCell(endCellLocation, currentCellLocation); i++) { 215 | cellsOnRow.add(excelCurrentCellIndex, ""); 216 | excelCurrentCellIndex++; 217 | } 218 | } 219 | try { 220 | this.assembleData(); 221 | } catch (AllEmptyRowException e) { 222 | throw e; 223 | } catch (Exception e) { 224 | throw new ExcelBootException(e); 225 | } 226 | cellsOnRow.clear(); 227 | currentRowIndex++; 228 | dataCurrentCellIndex = -1; 229 | excelCurrentCellIndex = 0; 230 | previousCellLocation = null; 231 | currentCellLocation = null; 232 | } 233 | 234 | } 235 | 236 | /** 237 | * 根据c节点的t属性获取单元格格式 238 | * 根据c节点的s属性获取单元格样式,去styles.xml文件找相应样式 239 | * 240 | * @param cellType xml中单元格格式属性 241 | * @param cellStyleStr xml中样式属性 242 | */ 243 | private void setCellType(String cellType) { 244 | if ("inlineStr".equals(cellType)) { 245 | cellFormatStr = ExcelCellType.INLINESTR; 246 | } else if ("s".equals(cellType) || cellType == null) { 247 | cellFormatStr = ExcelCellType.STRING; 248 | } else { 249 | throw new ExcelBootException("Excel单元格格式未设置成文本或者常规!单元格格式:{}", cellType); 250 | } 251 | } 252 | 253 | /** 254 | * 根据数据类型获取数据 255 | * 256 | * @param value 257 | * @return 258 | */ 259 | private String getCellValue(String value) { 260 | switch (cellFormatStr) { 261 | case INLINESTR: 262 | return new XSSFRichTextString(value).toString(); 263 | default: 264 | return String.valueOf(value); 265 | } 266 | } 267 | 268 | private void assembleData() throws Exception { 269 | if (currentRowIndex >= beginReadRowIndex) { 270 | List propertyList = excelMapping.getPropertyList(); 271 | for (int i = 0; i < propertyList.size() - cellsOnRow.size(); i++) { 272 | cellsOnRow.add(i, ""); 273 | } 274 | if (isAllEmptyRowData()) { 275 | throw new AllEmptyRowException("第{}行为空行,第{}个Sheet导入结束!", currentRowIndex + 1, currentSheetIndex + 1); 276 | } 277 | Object entity = excelClass.newInstance(); 278 | ErrorEntity errorEntity = ErrorEntity.builder().build(); 279 | for (int i = 0; i < propertyList.size(); i++) { 280 | dataCurrentCellIndex = i; 281 | Object cellValue = cellsOnRow.get(i); 282 | ExcelPropertyEntity property = propertyList.get(i); 283 | 284 | errorEntity = checkCellValue(i, property, cellValue); 285 | if (errorEntity.getErrorMessage() != null) { 286 | break; 287 | } 288 | cellValue = convertCellValue(property, cellValue); 289 | if (cellValue != null) { 290 | Field field = property.getFieldEntity(); 291 | field.set(entity, cellValue); 292 | } 293 | } 294 | if (errorEntity.getErrorMessage() == null) { 295 | importFunction.onProcess(currentSheetIndex + 1, currentRowIndex + 1, entity); 296 | } else { 297 | importFunction.onError(errorEntity); 298 | } 299 | } 300 | } 301 | 302 | private boolean isAllEmptyRowData() { 303 | int emptyCellCount = 0; 304 | for (Object cellData : cellsOnRow) { 305 | if (StringUtil.isBlank(cellData)) { 306 | emptyCellCount++; 307 | } 308 | } 309 | return emptyCellCount == cellsOnRow.size(); 310 | } 311 | 312 | private Object convertCellValue(ExcelPropertyEntity mappingProperty, Object cellValue) throws ParseException, ExecutionException { 313 | Class filedClazz = mappingProperty.getFieldEntity().getType(); 314 | if (filedClazz == Date.class) { 315 | if (!StringUtil.isBlank(cellValue)) { 316 | cellValue = parse(mappingProperty.getDateFormat(), cellValue.toString()); 317 | } else { 318 | cellValue = null; 319 | } 320 | } else if (filedClazz == Short.class || filedClazz == short.class) { 321 | cellValue = Short.valueOf(convertNullTOZERO(cellValue)); 322 | } else if (filedClazz == Integer.class || filedClazz == int.class) { 323 | cellValue = Integer.valueOf(convertNullTOZERO(cellValue)); 324 | } else if (filedClazz == Double.class || filedClazz == double.class) { 325 | cellValue = Double.valueOf(convertNullTOZERO(cellValue)); 326 | } else if (filedClazz == Long.class || filedClazz == long.class) { 327 | cellValue = Long.valueOf(convertNullTOZERO(cellValue)); 328 | } else if (filedClazz == Float.class || filedClazz == float.class) { 329 | cellValue = Float.valueOf(convertNullTOZERO(cellValue)); 330 | } else if (filedClazz == BigDecimal.class) { 331 | if (mappingProperty.getScale() == -1) { 332 | cellValue = new BigDecimal(convertNullTOZERO(cellValue)); 333 | } else { 334 | cellValue = new BigDecimal(convertNullTOZERO(cellValue)).setScale(mappingProperty.getScale(), mappingProperty.getRoundingMode()); 335 | } 336 | } else if (filedClazz != String.class) { 337 | throw new ExcelBootException("不支持的属性类型:{},导入失败!", filedClazz); 338 | } 339 | 340 | return cellValue; 341 | } 342 | 343 | 344 | private ErrorEntity checkCellValue(Integer cellIndex, ExcelPropertyEntity mappingProperty, Object cellValue) throws Exception { 345 | Boolean required = mappingProperty.getRequired(); 346 | if (null != required && required) { 347 | if (null == cellValue || StringUtil.isBlank(cellValue)) { 348 | String validErrorMessage = String.format("第[%s]个Sheet,第[%s]行,第[%s]列必填单元格为空!" 349 | , currentSheetIndex + 1, currentRowIndex + 1, cellIndex + 1); 350 | return buildErrorMsg(cellIndex, cellValue, validErrorMessage); 351 | } 352 | } 353 | 354 | String regex = mappingProperty.getRegex(); 355 | if (!StringUtil.isBlank(cellValue) && !StringUtil.isBlank(regex)) { 356 | boolean matches = isMatch(regex, cellValue.toString()); 357 | if (!matches) { 358 | String regularExpMessage = mappingProperty.getRegexMessage(); 359 | String validErrorMessage = String.format("第[%s]个Sheet,第[%s]行,第[%s]列,单元格值:[%s],正则表达式[%s]校验失败!" 360 | , currentSheetIndex + 1, currentRowIndex + 1, cellIndex + 1, cellValue, regularExpMessage); 361 | return buildErrorMsg(cellIndex, cellValue, validErrorMessage); 362 | } 363 | } 364 | 365 | return buildErrorMsg(cellIndex, cellValue, null); 366 | } 367 | 368 | private ErrorEntity buildErrorMsg(Integer cellIndex, Object cellValue, 369 | String validErrorMessage) { 370 | return ErrorEntity.builder() 371 | .sheetIndex(currentSheetIndex + 1) 372 | .rowIndex(currentRowIndex + 1) 373 | .cellIndex(cellIndex + 1) 374 | .cellValue(StringUtil.convertNull(cellValue)) 375 | .errorMessage(validErrorMessage) 376 | .build(); 377 | } 378 | 379 | /** 380 | * 计算两个单元格之间的单元格数目(同一行) 381 | * 382 | * @param refA 383 | * @param refB 384 | * @return 385 | */ 386 | public int countNullCell(String refA, String refB) { 387 | String xfdA = refA.replaceAll("\\d+", ""); 388 | String xfdB = refB.replaceAll("\\d+", ""); 389 | 390 | xfdA = fillChar(xfdA, 3, '@', true); 391 | xfdB = fillChar(xfdB, 3, '@', true); 392 | 393 | char[] letterA = xfdA.toCharArray(); 394 | char[] letterB = xfdB.toCharArray(); 395 | int res = (letterA[0] - letterB[0]) * 26 * 26 + (letterA[1] - letterB[1]) * 26 + (letterA[2] - letterB[2]); 396 | return res - 1; 397 | } 398 | 399 | private String fillChar(String str, int len, char let, boolean isPre) { 400 | int lenA = str.length(); 401 | if (lenA < len) { 402 | if (isPre) { 403 | StringBuilder strBuilder = new StringBuilder(str); 404 | for (int i = 0; i < (len - lenA); i++) { 405 | strBuilder.insert(0, let); 406 | } 407 | str = strBuilder.toString(); 408 | } else { 409 | StringBuilder strBuilder = new StringBuilder(str); 410 | for (int i = 0; i < (len - lenA); i++) { 411 | strBuilder.append(let); 412 | } 413 | str = strBuilder.toString(); 414 | } 415 | } 416 | return str; 417 | } 418 | 419 | 420 | /** 421 | * 单元格中的数据可能的数据类型 422 | */ 423 | enum ExcelCellType { 424 | INLINESTR, STRING, NULL 425 | } 426 | 427 | 428 | } 429 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | 3 |

4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 |

20 |

21 | 22 | **Excel-Boot是一款Excel导入导出解决方案组成的轻量级开源组件。** 23 | 24 | **如果喜欢或愿意使用, 请star本项目或者点击donate图标捐赠我们** 25 | 26 | **如果是企业使用, 为了产品推广, 请通过评论、Issue、PullRequest README的合作企业告诉我们企业名称** 27 | 28 | **请先仔细阅读本说明, 然后如果有任何问题或者建议可以通过issue告知我们, 尽力第一时间解决您的问题** 29 | ## 合作企业: 30 | 31 | ## 开源库地址(同步更新): 32 | 33 | GitHub: 34 | 35 | 码云: 36 | 37 | ## 功能简介 38 | 1. 浏览器导出Excel文件(支持单/多sheet) 39 | 40 | 2. 浏览器导出Excel模板文件 41 | 42 | 3. 指定路径生成Excel文件(支持单/多sheet) 43 | 44 | 4. 返回Excel文件(支持单/多sheet)的OutputStream, 一般用于将Excel文件上传到远程, 例如FTP 45 | 46 | 5. 导入Excel文件(支持单/多sheet) 47 | 48 | ## 功能强大 49 | 1.解决导出大量数据造成的内存溢出问题(支持分页查询数据库、采用poi官方推荐api(SXSSFWorkbook), 实现指定行数刷新到磁盘) 50 | 51 | 2.解决导入大量数据造成的内存溢出问题(支持分页插入数据库、采用poi官方推荐api(XSSF and SAX),采用SAX模式一行行读取到内存当中去) 52 | 53 | 3.解决含有占位符的空假行造成的读空值问题 54 | 55 | 4.解决Long类型或者BigDecimal的精度不准问题 56 | 57 | ## 组件特色 58 | 1.导入可以自定义解析成功或失败的处理逻辑 59 | 60 | 2.导出支持分页查询、全量查询, 自定义每条数据的处理逻辑 61 | 62 | 3.内置缓存, 3万条11列数据, 排除查询数据所用时间, 第一次导出2.2s左右、第二次导出在1.4s左右;第一次导入3.5s左右、第二次导入2.5s左右 63 | 64 | 4.注解操作, 轻量且便捷 65 | 66 | 5.内置常用正则表达式类RegexConst(身份证号、手机号、金额、邮件) 67 | 68 | 6.可配置是否适配单元格宽度, 默认开启(单元格内容超过20个汉字不再增加宽度, 3万条11列数据, 耗时50ms左右, 用时与数据量成正比) 69 | 70 | 7.假如出现异常,Sheet、行、列位置也都一并打印 71 | 72 | 8.注解中的用户自定义字符串信息以及Excel信息已全部trim,不用担心存在前后空格的风险 73 | 74 | 9.Excel样式简洁、大方、美观 75 | 76 | 10.导出的单条数据假如全部属性都为null或0或0.0或0.00或空字符串者null字符串,自动忽略,此特性也可让用户自定义忽略规则 77 | 78 | 11.除了直接返回OutputStream的方法以外的导出方法, 正常或异常情况都会自动关闭OutputStrem、Workbook流 79 | 80 | ## 组件需知 81 | ### 导入&导出 82 | 1.导入和导出只支持尾缀为xlsx的Excel文件 83 | 84 | 2.标注注解的属性顺序即Excel列的排列顺序 85 | 86 | 3.时间转化格式(dateFormat)默认为“yyyy-MM-dd HH:mm:ss“ 87 | 88 | ### 导入 89 | 1.当导入Excel, 读取到空行, 则停止读取当前Sheet的后面数据行 90 | 91 | 2.导入Excel文件, 单元格格式使用文本或者常规, 防止出现不可预测异常 92 | 93 | 3.导入字段类型支持:Date、Short(short)、Integer(int)、Double(double)、Long(long)、Float(float)、BigDecimal、String类型 94 | 95 | 4.导入BigDecimal字段精度默认为2, roundingMode默认为BigDecimal.ROUND_HALF_EVEN, scale设置为-1则不进行格式化 96 | 97 | 5.第一行有效单元格内必须包含内容并且以第一行为依据, 导入Excel文件列数必须等于标注注解的属性数量 98 | 99 | 6.Date类型字段,Excel与时间转化格式(dateFormat)相比,格式要保持一致(反例:2018/12/31和“yyyy-MM-dd“)并且长度要一致或更长(反例:"2018-12-31"和yyyy-MM-dd HH:mm:ss"),否则SimpleDateFormat将解析失败,报 “Unparseable date:” 100 | 101 | ### 导出 102 | 1.导出BigDecimal字段默认不进行精度格式化 103 | 104 | 2.分页查询默认从第一页开始, 每页3000条 105 | 106 | 3.Excel每超过2000条数据, 将内存中的数据刷新到磁盘当中去 107 | 108 | 4.使用分Sheet导出方法, 每8万行数据分Sheet 109 | 110 | 5.当使用(exportResponse、exportStream、generateExcelStream)方法时, 当单个Sheet超过100万条则会分Sheet 111 | 112 | 6.标注属性类型要与数据库类型保持一致 113 | 114 | 7.如果想提高性能, 并且内存允许、并发导出量不大, 可以根据实际场景适量改变分页条数和磁盘刷新量 115 | 116 | ## 扩展 117 | 1.新建子类继承ExcelBoot类, 使用子类构造器覆盖以下默认参数, 作为通用配置 118 | 119 | 2.直接调用以下两个构造器, 用于临时修改配置 120 | ```java 121 | /** 122 | * HttpServletResponse 通用导出Excel构造器 123 | */ 124 | ExportBuilder(HttpServletResponse response, String fileName, Class excelClass, Integer pageSize, Integer rowAccessWindowSize, Integer recordCountPerSheet, Boolean openAutoColumWidth) 125 | /** 126 | * OutputStream 通用导出Excel构造器 127 | */ 128 | ExportBuilder(OutputStream outputStream, String fileName, Class excelClass, Integer pageSize, Integer rowAccessWindowSize, Integer recordCountPerSheet, Boolean openAutoColumWidth) 129 | ``` 130 | ```java 131 | /** 132 | * Excel自动刷新到磁盘的数量 133 | */ 134 | public static final int DEFAULT_ROW_ACCESS_WINDOW_SIZE = 2000; 135 | /** 136 | * 分页条数 137 | */ 138 | public static final int DEFAULT_PAGE_SIZE = 3000; 139 | /** 140 | * 分Sheet条数 141 | */ 142 | public static final int DEFAULT_RECORD_COUNT_PEER_SHEET = 80000; 143 | /** 144 | * 是否开启自动适配宽度 145 | */ 146 | public static final boolean OPEN_AUTO_COLUM_WIDTH = true; 147 | ``` 148 | ## 版本 149 | 当前为2.0版本, 新版本正在开发(包括:导出Word、PDF、单元格合并等) 150 | 151 | ## 使用手册 152 | 1.引入Maven依赖 153 | 154 | 2.将需要导出或者导入的实体属性上标注@ExportField或@ImportField注解,并根据自己需要编写属性 155 | 156 | 3.在使用者项目相应的代码中粘贴相应的导出或者导入代码 157 | 158 | 4.将Demo中注释标明:"需要使用本组件的开发者自己定义的" 的替换成自己项目中的类 159 | 160 | 5.将开发者自定义逻辑编写到导出pageQuery或者导入onProcess方法中 161 | 162 | 6.将开发者自定义逻辑编写到导出convert或者导入onError方法中 163 | 164 | ### POM.xml 165 | 166 | ```xml 167 | 168 | io.github.magic-core 169 | excel-boot 170 | 2.0 171 | 172 | ``` 173 | ### 导出导入实体对象 174 | ```java 175 | /** 176 | * 导出导入实体对象 177 | * 178 | * 导出注解说明 179 | * columnName:导出Excel列名 180 | * scale:导出BigDecimal类型格式化精度 181 | * roundingMode:导出BigDecimal类型舍入规则 182 | * dateFormat:导出Data类型格式化模式 183 | * defaultCellValue:导出模板默认值 184 | * 185 | * 导入注解说明 186 | * required:是否非空校验 187 | * regex:正则校验规则 188 | * regexMessage:正则校验失败信息 189 | * scale:导出BigDecimal类型格式化精度 190 | * roundingMode:导出BigDecimal类型舍入规则 191 | * dateFormat:导出Data类型格式化模式 192 | */ 193 | public class UserEntity { 194 | /** 195 | * Integer类型字段 196 | */ 197 | @ExportField(columnName = "ID", defaultCellValue = "1") 198 | @ImportField(required = true) 199 | private Integer id; 200 | /** 201 | * String类型字段 202 | */ 203 | @ExportField(columnName = "姓名", defaultCellValue = "张三") 204 | @ImportField(regex = IDCARD_REGEX, regexMessage="身份证校验失败") 205 | private String name; 206 | /** 207 | * BigDecimal类型字段 208 | */ 209 | @ExportField(columnName = "收入金额", defaultCellValue = "100", scale = 2, roundingMode=BigDecimal.ROUND_HALF_EVEN) 210 | @ImportField(scale = 2, roundingMode=BigDecimal.ROUND_HALF_EVEN) 211 | private BigDecimal money; 212 | /** 213 | * Date类型字段 214 | */ 215 | @ExportField(columnName = "创建时间", dateFormat="yyyy-MM-dd", defaultCellValue = "2019-01-01") 216 | @ImportField(dateFormat="yyyy-MM-dd") 217 | private Date birthDayTime; 218 | } 219 | ``` 220 | ### 导入Demo 221 | ```java 222 | /** 223 | * 导入Demo 224 | * 225 | * UserEntity是标注注解的类, Excel的导入映射类, onProcess的userEntity参数则是Excel每行数据的映射实体 226 | * 需要使用本组件的开发者自己定义 227 | * 228 | * ErrorEntity是封装了每行Excel数据常规校验后的错误信息实体, 封装了sheet号、行号、列号、单元格值、所属列名、错误信息 229 | * 230 | * onProcess方法是用户自己实现, 当经过正则或者判空常规校验成功后执行的方法, 参数是每行数据映射的实体 231 | * onError方法是用户自己实现, 当经过正则或者判空常规校验失败后执行的方法 232 | */ 233 | @Controller 234 | @RequestMapping("/import") 235 | public class TestController { 236 | @RequestMapping("/importExcel") 237 | public void importExcel() throws IOException { 238 | ExcelBoot.ImportBuilder(new FileInputStream(new File("C:\\Users\\导入Excel文件.xlsx")), UserEntity.class) 239 | .importExcel(new ImportFunction() { 240 | 241 | /** 242 | * @param sheetIndex 当前执行的Sheet的索引, 从1开始 243 | * @param rowIndex 当前执行的行数, 从1开始 244 | * @param userEntity Excel行数据的实体 245 | */ 246 | @Override 247 | public void onProcess(int sheetIndex, int rowIndex, UserEntity userEntity) { 248 | //将读取到Excel中每一行的userEntity数据进行自定义处理 249 | //如果该行数据发生问题,将不会走本方法,而会走onError方法 250 | } 251 | 252 | /** 253 | * @param errorEntity 错误信息实体 254 | */ 255 | @Override 256 | public void onError(ErrorEntity errorEntity) { 257 | //将每条数据非空和正则校验后的错误信息errorEntity进行自定义处理 258 | } 259 | }); 260 | } 261 | } 262 | ``` 263 | ### 导出Demo 264 | ```java 265 | /** 266 | * 导出Demo 267 | * 268 | * UserEntity是标注注解的类,Excel映射的导出类 269 | * 需要使用本组件的开发者自己定义 270 | * ParamEntity是查询的参数对象 271 | * 需要使用本组件的开发者自己定义 272 | * ResultEntity是查询到的结果List内部元素 273 | * 需要使用本组件的开发者自己定义 274 | * 275 | * UserEntity可以和ResultEntity使用同一个对象,即直接在查询的结果对象上标注注解(建议使用两个对象, 实现解耦) 276 | * 277 | * pageQuery方法是用户自己实现, 即导出Excel的数据来源, 根据查询条件和当前页数和每页条数进行数据层查询, 当返回List的条数为NULL或者小于DEFAULT_PAGE_SIZE(每页条数)时, 将视为查询结束, 反之则会发生死循环 278 | * convert方法是用户自己实现, 参数就是您查询出来的list中的每个元素引用, 您可以对对象属性的转换或者对象的转换, 但是必须返回标注注解的对象 279 | */ 280 | @Controller 281 | @RequestMapping("/export") 282 | public class TestController { 283 | /** 284 | * 浏览器导出Excel 285 | * 286 | * @param httpServletResponse 287 | */ 288 | @RequestMapping("/exportResponse") 289 | public void exportResponse(HttpServletResponse httpServletResponse) { 290 | ParamEntity queryQaram = new ParamEntity(); 291 | ExcelBoot.ExportBuilder(httpServletResponse, "Excel文件名", UserEntity.class).exportResponse(queryQaram, 292 | new ExportFunction() { 293 | /** 294 | * @param queryQaram 查询条件对象 295 | * @param pageNum 当前页数,从1开始 296 | * @param pageSize 每页条数,默认3000 297 | * @return 298 | */ 299 | @Override 300 | public List pageQuery(ParamEntity queryQaram, int pageNum, int pageSize) { 301 | //1.将pageNum和pageSize传入使用本组件的开发者自己项目的分页逻辑中 302 | 303 | //2.调用使用本组件的开发者自己项目的分页查询方法 304 | List result=dao().queryPage(queryQaram); 305 | 306 | return result; 307 | } 308 | 309 | /** 310 | * 将查询出来的每条数据进行转换 311 | * 312 | * @param o 313 | */ 314 | @Override 315 | public UserEntity convert(ResultEntity o) { 316 | //用于编写开发者自定义的转换逻辑 317 | return new UserEntity(); 318 | } 319 | }); 320 | } 321 | 322 | /** 323 | * 浏览器多sheet导出Excel 324 | * 325 | * @param httpServletResponse 326 | */ 327 | @RequestMapping("/exportMultiSheetResponse") 328 | public void exportMultiSheetResponse(HttpServletResponse httpServletResponse) { 329 | ParamEntity queryQaram = new ParamEntity(); 330 | ExcelBoot.ExportBuilder(httpServletResponse, "Excel文件名", UserEntity.class).exportMultiSheetStream(queryQaram, 331 | new ExportFunction() { 332 | /** 333 | * @param queryQaram 查询条件对象 334 | * @param pageNum 当前页数,从1开始 335 | * @param pageSize 每页条数,默认3000 336 | * @return 337 | */ 338 | @Override 339 | public List pageQuery(ParamEntity queryQaram, int pageNum, int pageSize) { 340 | 341 | //1.将pageNum和pageSize传入使用本组件的开发者自己项目的分页逻辑中 342 | 343 | //2.调用使用本组件的开发者自己项目的分页查询方法 344 | List result=dao().queryPage(queryQaram); 345 | return result; 346 | } 347 | 348 | /** 349 | * 将查询出来的每条数据进行转换 350 | * 351 | * @param o 352 | */ 353 | @Override 354 | public UserEntity convert(ResultEntity o) { 355 | //用于编写开发者自定义的转换逻辑 356 | return new UserEntity(); 357 | } 358 | }); 359 | } 360 | 361 | /** 362 | * 导出Excel到指定路径 363 | */ 364 | @RequestMapping("/exportStream") 365 | public void exportStream() throws FileNotFoundException { 366 | ParamEntity queryQaram = new ParamEntity(); 367 | ExcelBoot.ExportBuilder(new FileOutputStream(new File("C:\\Users\\Excel文件.xlsx")), "Sheet名", UserEntity.class) 368 | .exportStream(queryQaram, new ExportFunction() { 369 | /** 370 | * @param queryQaram 查询条件对象 371 | * @param pageNum 当前页数,从1开始 372 | * @param pageSize 每页条数,默认3000 373 | * @return 374 | */ 375 | @Override 376 | public List pageQuery(ParamEntity queryQaram, int pageNum, int pageSize) { 377 | 378 | //1.将pageNum和pageSize传入使用本组件的开发者自己项目的分页逻辑中 379 | 380 | //2.调用使用本组件的开发者自己项目的分页查询方法 381 | List result=dao().queryPage(queryQaram); 382 | return result; 383 | } 384 | 385 | /** 386 | * 将查询出来的每条数据进行转换 387 | * 388 | * @param o 389 | */ 390 | @Override 391 | public UserEntity convert(ResultEntity o) { 392 | //用于编写开发者自定义的转换逻辑 393 | return new UserEntity(); 394 | } 395 | }); 396 | } 397 | 398 | /** 399 | * 导出多sheet Excel到指定路径 400 | */ 401 | @RequestMapping(value = "exportMultiSheetStream") 402 | public void exportMultiSheetStream() throws FileNotFoundException { 403 | ParamEntity queryQaram = new ParamEntity(); 404 | ExcelBoot.ExportBuilder(new FileOutputStream(new File("C:\\Users\\Excel文件.xlsx")), "Sheet名", UserEntity.class) 405 | .exportMultiSheetStream(queryQaram, new ExportFunction() { 406 | /** 407 | * @param queryQaram 查询条件对象 408 | * @param pageNum 当前页数,从1开始 409 | * @param pageSize 每页条数,默认3000 410 | * @return 411 | */ 412 | @Override 413 | public List pageQuery(ParamEntity queryQaram, int pageNum, int pageSize) { 414 | 415 | //1.将pageNum和pageSize传入使用本组件的开发者自己项目的分页逻辑中 416 | 417 | //2.调用使用本组件的开发者自己项目的分页查询方法 418 | List result=dao().queryPage(queryQaram); 419 | return result; 420 | } 421 | 422 | /** 423 | * 将查询出来的每条数据进行转换 424 | * 425 | * @param o 426 | */ 427 | @Override 428 | public UserEntity convert(ResultEntity o) { 429 | //用于编写开发者自定义的转换逻辑 430 | return new UserEntity(); 431 | } 432 | }); 433 | } 434 | 435 | /** 436 | * 生成Excel OutputStream对象 437 | */ 438 | @RequestMapping(generateStream) 439 | public void generateExcelStream() throws FileNotFoundException { 440 | ParamEntity queryQaram = new ParamEntity(); 441 | OutputStream outputStream = ExcelBoot.ExportBuilder(new FileOutputStream(new File("C:\\Users\\Excel文件.xlsx")), "Sheet名", UserEntity.class) 442 | .generateExcelStream(queryQaram, new ExportFunction() { 443 | /** 444 | * @param queryQaram 查询条件对象 445 | * @param pageNum 当前页数,从1开始 446 | * @param pageSize 每页条数,默认3000 447 | * @return 448 | */ 449 | @Override 450 | public List pageQuery(ParamEntity queryQaram, int pageNum, int pageSize) { 451 | 452 | //1.将pageNum和pageSize传入使用本组件的开发者自己项目的分页逻辑中 453 | 454 | //2.调用使用本组件的开发者自己项目的分页查询方法 455 | List result=dao().queryPage(queryQaram); 456 | return result; 457 | } 458 | 459 | /** 460 | * 将查询出来的每条数据进行转换 461 | * 462 | * @param o 463 | */ 464 | @Override 465 | public UserEntity convert(ResultEntity o) { 466 | //用于编写开发者自定义的转换逻辑 467 | return new UserEntity(); 468 | } 469 | }); 470 | } 471 | 472 | /** 473 | * 生成多Sheet Excel OutputStream对象 474 | */ 475 | @RequestMapping(generateMultiSheetStream) 476 | public void generateMultiSheetExcelStream() throws FileNotFoundException { 477 | ParamEntity queryQaram = new ParamEntity(); 478 | OutputStream outputStream = ExcelBoot.ExportBuilder(new FileOutputStream(new File("C:\\Users\\Excel文件.xlsx")), "Sheet名", UserEntity.class) 479 | .generateMultiSheetExcelStream(queryQaram, new ExportFunction() { 480 | /** 481 | * @param queryQaram 查询条件对象 482 | * @param pageNum 当前页数,从1开始 483 | * @param pageSize 每页条数,默认3000 484 | * @return 485 | */ 486 | @Override 487 | public List pageQuery(ParamEntity queryQaram, int pageNum, int pageSize) { 488 | 489 | //1.将pageNum和pageSize传入使用本组件的开发者自己项目的分页逻辑中 490 | 491 | //2.调用使用本组件的开发者自己项目的分页查询方法 492 | List result=dao().queryPage(queryQaram); 493 | return result; 494 | } 495 | 496 | /** 497 | * 将查询出来的每条数据进行转换 498 | * 499 | * @param o 500 | */ 501 | @Override 502 | public UserEntity convert(ResultEntity o) { 503 | //用于编写开发者自定义的转换逻辑 504 | return new UserEntity(); 505 | } 506 | }); 507 | } 508 | 509 | /** 510 | * 导出Excel模板 511 | */ 512 | @RequestMapping("/exportTemplate") 513 | public void exportTemplate(HttpServletResponse httpServletResponse) { 514 | ExcelBoot.ExportBuilder(httpServletResponse, "Excel模板名称", UserEntity.class).exportTemplate(); 515 | } 516 | } 517 | ``` 518 | --------------------------------------------------------------------------------