• 生成带分表和水印的excel压缩文件


    功能描述

    将查询结果生成带分表和水印的excel压缩文件


    功能点

    1、将查询结果导出为excel文件

    2、每个表格存放50万条数据,超过50万条数据,生成新的分表

    3、生成的表格需要添加水印

    4、将生成的全部分表,打包成zip压缩文件


    引入依赖

    1. <dependency>
    2. <groupId>com.alibabagroupId>
    3. <artifactId>easyexcelartifactId>
    4. <version>3.3.2version>
    5. <exclusions>
    6. <exclusion>
    7. <artifactId>commons-compressartifactId>
    8. <groupId>org.apache.commonsgroupId>
    9. exclusion>
    10. exclusions>
    11. dependency>
    12. <dependency>
    13. <groupId>org.apache.poigroupId>
    14. <artifactId>ooxml-schemasartifactId>
    15. <version>1.4version>
    16. dependency>
    17. <dependency>
    18. <groupId>net.lingala.zip4jgroupId>
    19. <artifactId>zip4jartifactId>
    20. <version>1.3.2version>
    21. dependency>
    22. <dependency>
    23. <groupId>cn.hutoolgroupId>
    24. <artifactId>hutool-allartifactId>
    25. <version>5.8.19version>
    26. <scope>compilescope>
    27. dependency>
    1. package com.cusc.product.common.util.excel;
    2. import com.alibaba.excel.EasyExcel;
    3. import com.alibaba.excel.EasyExcelFactory;
    4. import com.alibaba.excel.ExcelWriter;
    5. import com.alibaba.excel.write.metadata.WriteSheet;
    6. import com.alibaba.excel.write.style.column.LongestMatchColumnWidthStyleStrategy;
    7. import com.cusc.product.common.util.WaterMarkCommonUtil;
    8. import lombok.extern.slf4j.Slf4j;
    9. import org.apache.commons.collections4.CollectionUtils;
    10. import org.apache.commons.lang3.StringUtils;
    11. import org.apache.poi.ss.usermodel.Workbook;
    12. import java.io.File;
    13. import java.io.IOException;
    14. import java.util.LinkedHashSet;
    15. import java.util.List;
    16. import java.util.Objects;
    17. import java.util.Set;
    18. import java.util.UUID;
    19. import java.util.function.Supplier;
    20. /**
    21. * Excel文件生成
    22. *
    23. * @author liubin
    24. */
    25. @Slf4j
    26. public class EasyExcelWriteCommonUtil {
    27. /**
    28. * 文件行数限制
    29. */
    30. private static final int EXCEL_LIMIT_NUM = 500000;
    31. /**
    32. * 数据写入Excel文件,多文件 自定义表头
    33. *
    34. * @param suppliers 自定义函数
    35. * @param listFiled 表头
    36. * @param dirName 期待的压缩文件的名称
    37. * @return zip压缩后的文件
    38. */
    39. public static File createMultiFileExcelZipFiles(List> suppliers, List listFiled,
    40. Class clzss, String dirName, String waterMarkUserName, String waterMarkUsePhone) {
    41. File zipFile;
    42. // 创建临时目录
    43. if (Objects.isNull(dirName)) {
    44. dirName = UUID.randomUUID().toString();
    45. }
    46. File localDir = createDir(FileBaseCommonUtil.TEMPORARY_DIRECTORY_PATH + "/" + dirName);
    47. log.info("临时目录: " + localDir.getPath());
    48. String sheet = "Sheet1";
    49. // 文件批次编号
    50. int fileId = 1;
    51. // 单个文件累积写入数据行数
    52. int number = 0;
    53. ExcelWriter excelWriter = null;
    54. WriteSheet writeSheet = null;
    55. //只导出这几列
    56. Set includeColumnFiledNames = new LinkedHashSet<>();
    57. for (int j = 0; j < listFiled.size(); j++) {
    58. includeColumnFiledNames.add(listFiled.get(j));
    59. }
    60. try {
    61. for (int i = 0; i < suppliers.size(); i++) {
    62. List dataList = suppliers.get(i).get();
    63. if (dataList.size() > EXCEL_LIMIT_NUM) {
    64. log.error("Supplier返回数据量最大不可超过{}", EXCEL_LIMIT_NUM);
    65. throw new RuntimeException(String.format("Supplier返回数据量最大不可超过%s", EXCEL_LIMIT_NUM));
    66. }
    67. number += dataList.size();
    68. if (number > EXCEL_LIMIT_NUM) {
    69. // 数据量累积超出了, 需要新建文件记录
    70. int comNum = number - EXCEL_LIMIT_NUM;
    71. List compensate = dataList.subList(dataList.size() - comNum, dataList.size());
    72. List oldDataList = dataList.subList(0, dataList.size() - comNum);
    73. // 旧文件追加数据
    74. File oldFile = new File(localDir, dirName + "_" + fileId + ".xlsx");
    75. if (oldFile.exists()) {
    76. excelWriter.write(oldDataList, writeSheet);
    77. // 添加水印
    78. Workbook workbook = excelWriter.writeContext().writeWorkbookHolder().getWorkbook();
    79. WaterMarkCommonUtil.insertWaterMarkNamePhoneToXlsx(workbook, waterMarkUserName, waterMarkUsePhone);
    80. excelWriter.finish();
    81. }
    82. if (CollectionUtils.isNotEmpty(compensate)) {
    83. // 写入新的文件数据
    84. File file = new File(localDir, dirName + "_" + (++fileId) + ".xlsx");
    85. excelWriter = EasyExcelFactory.write(file.getAbsolutePath()).build();
    86. writeSheet = EasyExcel.writerSheet(i, sheet).head(clzss).includeColumnFieldNames(includeColumnFiledNames).registerWriteHandler(new LongestMatchColumnWidthStyleStrategy()).build();
    87. // writeSheet = EasyExcelFactory.writerSheet(sheet).build();
    88. excelWriter.write(dataList, writeSheet);
    89. // 添加水印
    90. Workbook workbook = excelWriter.writeContext().writeWorkbookHolder().getWorkbook();
    91. WaterMarkCommonUtil.insertWaterMarkNamePhoneToXlsx(workbook, waterMarkUserName, waterMarkUsePhone);
    92. // 重置记录数
    93. number = compensate.size();
    94. }
    95. if (CollectionUtils.isNotEmpty(oldDataList)) {
    96. oldDataList.clear();
    97. }
    98. if (CollectionUtils.isNotEmpty(compensate)) {
    99. compensate.clear();
    100. }
    101. if (CollectionUtils.isNotEmpty(dataList)) {
    102. dataList.clear();
    103. }
    104. continue;
    105. }
    106. File file = new File(localDir, dirName + "_" + fileId + ".xlsx");
    107. if (file.exists()) {
    108. excelWriter.write(dataList, writeSheet);
    109. // 添加水印
    110. Workbook workbook = excelWriter.writeContext().writeWorkbookHolder().getWorkbook();
    111. WaterMarkCommonUtil.insertWaterMarkNamePhoneToXlsx(workbook, waterMarkUserName, waterMarkUsePhone);
    112. } else {
    113. excelWriter = EasyExcelFactory.write(file.getAbsolutePath()).build();
    114. writeSheet = EasyExcel.writerSheet(i, sheet).head(clzss).includeColumnFieldNames(includeColumnFiledNames).registerWriteHandler(new LongestMatchColumnWidthStyleStrategy()).build();
    115. excelWriter.write(dataList, writeSheet);
    116. // 添加水印
    117. Workbook workbook = excelWriter.writeContext().writeWorkbookHolder().getWorkbook();
    118. WaterMarkCommonUtil.insertWaterMarkNamePhoneToXlsx(workbook, waterMarkUserName, waterMarkUsePhone);
    119. }
    120. if (CollectionUtils.isNotEmpty(dataList)) {
    121. dataList.clear();
    122. }
    123. }
    124. } catch (RuntimeException e) {
    125. log.error("数据导出失败", e);
    126. throw new RuntimeException(e);
    127. } catch (IOException e) {
    128. log.error("数据导出失败", e);
    129. throw new RuntimeException(e);
    130. } finally {
    131. if (excelWriter != null) {
    132. excelWriter.finish();
    133. }
    134. }
    135. String zipPath = Zip4jCommonUtil.zip(localDir.getPath());
    136. deleteFile(localDir);
    137. if (StringUtils.isEmpty(zipPath)) {
    138. log.error("文件压缩失败");
    139. throw new RuntimeException("文件压缩失败");
    140. }
    141. zipFile = new File(zipPath);
    142. log.info("临时目录压缩后路径: " + zipPath);
    143. return zipFile;
    144. }
    145. /**
    146. * 删除文件目录
    147. *
    148. * @param file
    149. * @return
    150. */
    151. private static Boolean deleteFile(File file) {
    152. // 判断文件不为null或文件目录存在
    153. if (file == null || !file.exists()) {
    154. log.error("文件删除失败,请检查文件是否存在以及文件路径是否正确");
    155. return false;
    156. }
    157. // 获取目录下子文件
    158. File[] files = file.listFiles();
    159. // 遍历该目录下的文件对象
    160. for (File f : files) {
    161. // 判断子目录是否存在子目录,如果是文件则删除
    162. if (f.isDirectory()) {
    163. // 递归删除目录下的文件
    164. deleteFile(f);
    165. } else {
    166. // 文件删除
    167. f.delete();
    168. }
    169. }
    170. // 文件夹删除
    171. file.delete();
    172. return true;
    173. }
    174. /**
    175. * 创建目录
    176. *
    177. * @param localDirPath 目录路径
    178. * @return File对象
    179. */
    180. private static File createDir(String localDirPath) {
    181. File localDir = new File(localDirPath);
    182. if (!localDir.exists()) {
    183. localDir.mkdirs();
    184. }
    185. return localDir;
    186. }
    187. }
    1. package com.cusc.product.common.util;
    2. import cn.hutool.core.date.DateUtil;
    3. import cn.hutool.core.util.ReflectUtil;
    4. import org.apache.poi.openxml4j.opc.PackagePartName;
    5. import org.apache.poi.openxml4j.opc.PackageRelationship;
    6. import org.apache.poi.openxml4j.opc.TargetMode;
    7. import org.apache.poi.ss.usermodel.Workbook;
    8. import org.apache.poi.xssf.streaming.SXSSFSheet;
    9. import org.apache.poi.xssf.streaming.SXSSFWorkbook;
    10. import org.apache.poi.xssf.usermodel.XSSFPictureData;
    11. import org.apache.poi.xssf.usermodel.XSSFRelation;
    12. import org.apache.poi.xssf.usermodel.XSSFSheet;
    13. import org.apache.poi.xssf.usermodel.XSSFWorkbook;
    14. import javax.imageio.ImageIO;
    15. import java.awt.*;
    16. import java.awt.image.BufferedImage;
    17. import java.io.ByteArrayOutputStream;
    18. import java.io.IOException;
    19. public class WaterMarkCommonUtil {
    20. /**
    21. * Excel 导出添加水印
    22. *
    23. * @param workbook ExcelWorkbook
    24. * @param userName 导出人姓名
    25. */
    26. public static void insertWaterMarkNamePhoneToXlsx(Workbook workbook, String userName, String phone) throws IOException {
    27. String date = DateUtil.now();
    28. String waterMarkText = userName + "\n" + phone;
    29. if (workbook instanceof SXSSFWorkbook) {
    30. insertWaterMarkTextToXlsx((SXSSFWorkbook) workbook, waterMarkText);
    31. } else if (workbook instanceof XSSFWorkbook) {
    32. insertWaterMarkTextToXlsx((XSSFWorkbook) workbook, waterMarkText);
    33. }
    34. //throw new RemoteException("HSSFWorkbook 模式不支持 Excel 水印");
    35. }
    36. /**
    37. * Excel 导出添加水印
    38. *
    39. * @param workbook ExcelWorkbook
    40. * @param userName 导出人姓名
    41. */
    42. public static void insertWaterMarkTextToXlsx(Workbook workbook, String userName) throws IOException {
    43. String date = DateUtil.now();
    44. String waterMarkText = userName + "\n" + date;
    45. if (workbook instanceof SXSSFWorkbook) {
    46. insertWaterMarkTextToXlsx((SXSSFWorkbook) workbook, waterMarkText);
    47. } else if (workbook instanceof XSSFWorkbook) {
    48. insertWaterMarkTextToXlsx((XSSFWorkbook) workbook, waterMarkText);
    49. }
    50. //throw new RemoteException("HSSFWorkbook 模式不支持 Excel 水印");
    51. }
    52. /**
    53. * 给 Excel 添加水印
    54. *
    55. * @param workbook SXSSFWorkbook
    56. * @param waterMarkText 水印文字内容
    57. */
    58. private static void insertWaterMarkTextToXlsx(SXSSFWorkbook workbook, String waterMarkText) throws IOException {
    59. BufferedImage image = createWatermarkImage(waterMarkText);
    60. ByteArrayOutputStream imageOs = new ByteArrayOutputStream();
    61. ImageIO.write(image, "png", imageOs);
    62. int pictureIdx = workbook.addPicture(imageOs.toByteArray(), XSSFWorkbook.PICTURE_TYPE_PNG);
    63. XSSFPictureData pictureData = (XSSFPictureData) workbook.getAllPictures().get(pictureIdx);
    64. for (int i = 0; i < workbook.getNumberOfSheets(); i++) {//获取每个Sheet表
    65. SXSSFSheet sheet = workbook.getSheetAt(i);
    66. //这里由于 SXSSFSheet 没有 getCTWorksheet() 方法,通过反射取出 _sh 属性
    67. XSSFSheet shReflect = (XSSFSheet) ReflectUtil.getFieldValue(sheet, "_sh");
    68. PackagePartName ppn = pictureData.getPackagePart().getPartName();
    69. String relType = XSSFRelation.IMAGES.getRelation();
    70. PackageRelationship pr = shReflect.getPackagePart().addRelationship(ppn, TargetMode.INTERNAL, relType, null);
    71. shReflect.getCTWorksheet().addNewPicture().setId(pr.getId());
    72. }
    73. imageOs.close();
    74. }
    75. /**
    76. * 给 Excel 添加水印
    77. *
    78. * @param workbook XSSFWorkbook
    79. * @param waterMarkText 水印文字内容
    80. */
    81. private static void insertWaterMarkTextToXlsx(XSSFWorkbook workbook, String waterMarkText) throws IOException {
    82. BufferedImage image = createWatermarkImage(waterMarkText);
    83. ByteArrayOutputStream imageOs = new ByteArrayOutputStream();
    84. ImageIO.write(image, "png", imageOs);
    85. int pictureIdx = workbook.addPicture(imageOs.toByteArray(), XSSFWorkbook.PICTURE_TYPE_PNG);
    86. XSSFPictureData pictureData = workbook.getAllPictures().get(pictureIdx);
    87. for (int i = 0; i < workbook.getNumberOfSheets(); i++) {//获取每个Sheet表
    88. XSSFSheet sheet = workbook.getSheetAt(i);
    89. PackagePartName ppn = pictureData.getPackagePart().getPartName();
    90. String relType = XSSFRelation.IMAGES.getRelation();
    91. PackageRelationship pr = sheet.getPackagePart().addRelationship(ppn, TargetMode.INTERNAL, relType, null);
    92. sheet.getCTWorksheet().addNewPicture().setId(pr.getId());
    93. }
    94. imageOs.close();
    95. }
    96. /**
    97. * 创建水印图片
    98. *
    99. * @param waterMark 水印文字
    100. */
    101. private static BufferedImage createWatermarkImage(String waterMark) {
    102. String[] textArray = waterMark.split("\n");
    103. Font font = new Font("microsoft-yahei", Font.PLAIN, 24);
    104. // int width = 500;
    105. // int height = 400;
    106. int width = 300;
    107. int height = 200;
    108. BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
    109. // 背景透明 开始
    110. Graphics2D g = image.createGraphics();
    111. image = g.getDeviceConfiguration().createCompatibleImage(width, height, Transparency.TRANSLUCENT);
    112. g.dispose();
    113. // 背景透明 结束
    114. g = image.createGraphics();
    115. g.setColor(new Color(237, 235, 233));// 设定画笔颜色
    116. g.setFont(font);// 设置画笔字体
    117. // g.shear(0.1, -0.26);// 设定倾斜度
    118. // 设置字体平滑
    119. g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
    120. //文字从中心开始输入,算出文字宽度,左移动一半的宽度,即居中
    121. FontMetrics fontMetrics = g.getFontMetrics(font);
    122. // 水印位置
    123. int x = width / 2;
    124. int y = height / 2;
    125. // 设置水印旋转
    126. g.rotate(Math.toRadians(-40), x, y);
    127. for (String s : textArray) {
    128. // 文字宽度
    129. int textWidth = fontMetrics.stringWidth(s);
    130. g.drawString(s, x - (textWidth / 2), y);// 画出字符串
    131. y = y + font.getSize();
    132. }
    133. g.dispose();// 释放画笔
    134. return image;
    135. }
    136. }

  • 相关阅读:
    MM32F0020 UART1中断接收
    Linux的打包和压缩
    随手笔记-Jackson -> @JsonFormat -> 失真问题解决
    自动化测试框架(二)读取配置文件中内容
    麒麟桌面系统CVE-2024-1086漏洞修复
    Java.Integer.bitCount(int)源码解析
    HarmonyOS4.0系统性深入开发34栅格布局(GridRow/GridCol)
    第十章《日期与时间》第9节:日期时间对象的转换
    请求的接口响应状态为已取消的原因
    Spring IoC源码:createBean
  • 原文地址:https://blog.csdn.net/liubin5620/article/details/134216770