xm
2024-06-14 722af26bc6fec32bb289b1df51a9016a4935610f
提交 | 用户 | 时间
722af2 1 package com.dl.common.utils.poi;
X 2
3 import cn.hutool.core.collection.CollUtil;
4 import cn.hutool.core.io.resource.ClassPathResource;
5 import cn.hutool.core.util.IdUtil;
6 import com.alibaba.excel.EasyExcel;
7 import com.alibaba.excel.ExcelWriter;
8 import com.alibaba.excel.write.builder.ExcelWriterSheetBuilder;
9 import com.alibaba.excel.write.metadata.WriteSheet;
10 import com.alibaba.excel.write.metadata.fill.FillConfig;
11 import com.alibaba.excel.write.metadata.fill.FillWrapper;
12 import com.alibaba.excel.write.style.column.LongestMatchColumnWidthStyleStrategy;
13 import com.dl.common.convert.ExcelBigNumberConvert;
14 import com.dl.common.excel.CellMergeStrategy;
15 import com.dl.common.excel.DefaultExcelListener;
16 import com.dl.common.excel.ExcelListener;
17 import com.dl.common.excel.ExcelResult;
18 import com.dl.common.utils.StringUtils;
19 import com.dl.common.utils.file.FileUtils;
20 import lombok.AccessLevel;
21 import lombok.NoArgsConstructor;
22
23 import javax.servlet.ServletOutputStream;
24 import javax.servlet.http.HttpServletResponse;
25 import java.io.IOException;
26 import java.io.InputStream;
27 import java.io.OutputStream;
28 import java.io.UnsupportedEncodingException;
29 import java.util.Collection;
30 import java.util.List;
31 import java.util.Map;
32
33 /**
34  * Excel相关处理
35  *
36  * @author Lion Li
37  */
38 @NoArgsConstructor(access = AccessLevel.PRIVATE)
39 public class ExcelUtil {
40
41     /**
42      * 同步导入(适用于小数据量)
43      *
44      * @param is 输入流
45      * @return 转换后集合
46      */
47     public static <T> List<T> importExcel(InputStream is, Class<T> clazz) {
48         return EasyExcel.read(is).head(clazz).autoCloseStream(false).sheet().doReadSync();
49     }
50
51
52     /**
53      * 使用校验监听器 异步导入 同步返回
54      *
55      * @param is         输入流
56      * @param clazz      对象类型
57      * @param isValidate 是否 Validator 检验 默认为是
58      * @return 转换后集合
59      */
60     public static <T> ExcelResult<T> importExcel(InputStream is, Class<T> clazz, boolean isValidate) {
61         DefaultExcelListener<T> listener = new DefaultExcelListener<>(isValidate);
62         EasyExcel.read(is, clazz, listener).sheet().doRead();
63         return listener.getExcelResult();
64     }
65
66     /**
67      * 使用自定义监听器 异步导入 自定义返回
68      *
69      * @param is       输入流
70      * @param clazz    对象类型
71      * @param listener 自定义监听器
72      * @return 转换后集合
73      */
74     public static <T> ExcelResult<T> importExcel(InputStream is, Class<T> clazz, ExcelListener<T> listener) {
75         EasyExcel.read(is, clazz, listener).sheet().doRead();
76         return listener.getExcelResult();
77     }
78
79     /**
80      * 导出excel
81      *
82      * @param list      导出数据集合
83      * @param sheetName 工作表的名称
84      * @param clazz     实体类
85      * @param response  响应体
86      */
87     public static <T> void exportExcel(List<T> list, String sheetName, Class<T> clazz, HttpServletResponse response) {
88         try {
89             resetResponse(sheetName, response);
90             ServletOutputStream os = response.getOutputStream();
91             exportExcel(list, sheetName, clazz, false, os);
92         } catch (IOException e) {
93             throw new RuntimeException("导出Excel异常");
94         }
95     }
96
97     /**
98      * 导出excel
99      *
100      * @param list      导出数据集合
101      * @param sheetName 工作表的名称
102      * @param clazz     实体类
103      * @param merge     是否合并单元格
104      * @param response  响应体
105      */
106     public static <T> void exportExcel(List<T> list, String sheetName, Class<T> clazz, boolean merge, HttpServletResponse response) {
107         try {
108             resetResponse(sheetName, response);
109             ServletOutputStream os = response.getOutputStream();
110             exportExcel(list, sheetName, clazz, merge, os);
111         } catch (IOException e) {
112             throw new RuntimeException("导出Excel异常");
113         }
114     }
115
116     /**
117      * 导出excel
118      *
119      * @param list      导出数据集合
120      * @param sheetName 工作表的名称
121      * @param clazz     实体类
122      * @param os        输出流
123      */
124     public static <T> void exportExcel(List<T> list, String sheetName, Class<T> clazz, OutputStream os) {
125         exportExcel(list, sheetName, clazz, false, os);
126     }
127
128     /**
129      * 导出excel
130      *
131      * @param list      导出数据集合
132      * @param sheetName 工作表的名称
133      * @param clazz     实体类
134      * @param merge     是否合并单元格
135      * @param os        输出流
136      */
137     public static <T> void exportExcel(List<T> list, String sheetName, Class<T> clazz, boolean merge, OutputStream os) {
138         ExcelWriterSheetBuilder builder = EasyExcel.write(os, clazz)
139             .autoCloseStream(false)
140             // 自动适配
141             .registerWriteHandler(new LongestMatchColumnWidthStyleStrategy())
142             // 大数值自动转换 防止失真
143             .registerConverter(new ExcelBigNumberConvert())
144             .sheet(sheetName);
145         if (merge) {
146             // 合并处理器
147             builder.registerWriteHandler(new CellMergeStrategy(list, true));
148         }
149         builder.doWrite(list);
150     }
151
152     /**
153      * 单表多数据模板导出 模板格式为 {.属性}
154      *
155      * @param filename     文件名
156      * @param templatePath 模板路径 resource 目录下的路径包括模板文件名
157      *                     例如: excel/temp.xlsx
158      *                     重点: 模板文件必须放置到启动类对应的 resource 目录下
159      * @param data         模板需要的数据
160      * @param response     响应体
161      */
162     public static void exportTemplate(List<Object> data, String filename, String templatePath, HttpServletResponse response) {
163         try {
164             resetResponse(filename, response);
165             ServletOutputStream os = response.getOutputStream();
166             exportTemplate(data, templatePath, os);
167         } catch (IOException e) {
168             throw new RuntimeException("导出Excel异常");
169         }
170     }
171
172     /**
173      * 单表多数据模板导出 模板格式为 {.属性}
174      *
175      * @param templatePath 模板路径 resource 目录下的路径包括模板文件名
176      *                     例如: excel/temp.xlsx
177      *                     重点: 模板文件必须放置到启动类对应的 resource 目录下
178      * @param data         模板需要的数据
179      * @param os           输出流
180      */
181     public static void exportTemplate(List<Object> data, String templatePath, OutputStream os) {
182         ClassPathResource templateResource = new ClassPathResource(templatePath);
183         ExcelWriter excelWriter = EasyExcel.write(os)
184             .withTemplate(templateResource.getStream())
185             .autoCloseStream(false)
186             // 大数值自动转换 防止失真
187             .registerConverter(new ExcelBigNumberConvert())
188             .build();
189         WriteSheet writeSheet = EasyExcel.writerSheet().build();
190         if (CollUtil.isEmpty(data)) {
191             throw new IllegalArgumentException("数据为空");
192         }
193         // 单表多数据导出 模板格式为 {.属性}
194         for (Object d : data) {
195             excelWriter.fill(d, writeSheet);
196         }
197         excelWriter.finish();
198     }
199
200     /**
201      * 多表多数据模板导出 模板格式为 {key.属性}
202      *
203      * @param filename     文件名
204      * @param templatePath 模板路径 resource 目录下的路径包括模板文件名
205      *                     例如: excel/temp.xlsx
206      *                     重点: 模板文件必须放置到启动类对应的 resource 目录下
207      * @param data         模板需要的数据
208      * @param response     响应体
209      */
210     public static void exportTemplateMultiList(Map<String, Object> data, String filename, String templatePath, HttpServletResponse response) {
211         try {
212             resetResponse(filename, response);
213             ServletOutputStream os = response.getOutputStream();
214             exportTemplateMultiList(data, templatePath, os);
215         } catch (IOException e) {
216             throw new RuntimeException("导出Excel异常");
217         }
218     }
219
220     /**
221      * 多表多数据模板导出 模板格式为 {key.属性}
222      *
223      * @param templatePath 模板路径 resource 目录下的路径包括模板文件名
224      *                     例如: excel/temp.xlsx
225      *                     重点: 模板文件必须放置到启动类对应的 resource 目录下
226      * @param data         模板需要的数据
227      * @param os           输出流
228      */
229     public static void exportTemplateMultiList(Map<String, Object> data, String templatePath, OutputStream os) {
230         ClassPathResource templateResource = new ClassPathResource(templatePath);
231         ExcelWriter excelWriter = EasyExcel.write(os)
232             .withTemplate(templateResource.getStream())
233             .autoCloseStream(false)
234             // 大数值自动转换 防止失真
235             .registerConverter(new ExcelBigNumberConvert())
236             .build();
237         WriteSheet writeSheet = EasyExcel.writerSheet().build();
238         if (CollUtil.isEmpty(data)) {
239             throw new IllegalArgumentException("数据为空");
240         }
241         for (Map.Entry<String, Object> map : data.entrySet()) {
242             // 设置列表后续还有数据
243             FillConfig fillConfig = FillConfig.builder().forceNewRow(Boolean.TRUE).build();
244             if (map.getValue() instanceof Collection) {
245                 // 多表导出必须使用 FillWrapper
246                 excelWriter.fill(new FillWrapper(map.getKey(), (Collection<?>) map.getValue()), fillConfig, writeSheet);
247             } else {
248                 excelWriter.fill(map.getValue(), writeSheet);
249             }
250         }
251         excelWriter.finish();
252     }
253
254     /**
255      * 重置响应体
256      */
257     private static void resetResponse(String sheetName, HttpServletResponse response) throws UnsupportedEncodingException {
258         String filename = encodingFilename(sheetName);
259         FileUtils.setAttachmentResponseHeader(response, filename);
260         response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=UTF-8");
261     }
262
263     /**
264      * 解析导出值 0=男,1=女,2=未知
265      *
266      * @param propertyValue 参数值
267      * @param converterExp  翻译注解
268      * @param separator     分隔符
269      * @return 解析后值
270      */
271     public static String convertByExp(String propertyValue, String converterExp, String separator) {
272         StringBuilder propertyString = new StringBuilder();
273         String[] convertSource = converterExp.split(StringUtils.SEPARATOR);
274         for (String item : convertSource) {
275             String[] itemArray = item.split("=");
276             if (StringUtils.containsAny(propertyValue, separator)) {
277                 for (String value : propertyValue.split(separator)) {
278                     if (itemArray[0].equals(value)) {
279                         propertyString.append(itemArray[1] + separator);
280                         break;
281                     }
282                 }
283             } else {
284                 if (itemArray[0].equals(propertyValue)) {
285                     return itemArray[1];
286                 }
287             }
288         }
289         return StringUtils.stripEnd(propertyString.toString(), separator);
290     }
291
292     /**
293      * 反向解析值 男=0,女=1,未知=2
294      *
295      * @param propertyValue 参数值
296      * @param converterExp  翻译注解
297      * @param separator     分隔符
298      * @return 解析后值
299      */
300     public static String reverseByExp(String propertyValue, String converterExp, String separator) {
301         StringBuilder propertyString = new StringBuilder();
302         String[] convertSource = converterExp.split(StringUtils.SEPARATOR);
303         for (String item : convertSource) {
304             String[] itemArray = item.split("=");
305             if (StringUtils.containsAny(propertyValue, separator)) {
306                 for (String value : propertyValue.split(separator)) {
307                     if (itemArray[1].equals(value)) {
308                         propertyString.append(itemArray[0] + separator);
309                         break;
310                     }
311                 }
312             } else {
313                 if (itemArray[1].equals(propertyValue)) {
314                     return itemArray[0];
315                 }
316             }
317         }
318         return StringUtils.stripEnd(propertyString.toString(), separator);
319     }
320
321     /**
322      * 编码文件名
323      */
324     public static String encodingFilename(String filename) {
325         return IdUtil.fastSimpleUUID() + "_" + filename + ".xlsx";
326     }
327
328 }