ExcelUtil

296 阅读3分钟

依赖

<dependency>  
    <groupId>org.projectlombok</groupId>  
    <artifactId>lombok</artifactId>  
    <optional>true</optional>  
</dependency>

<dependency>
    <groupId>cn.hutool</groupId>
    <artifactId>hutool-all</artifactId>
    <version>5.8.10</version>
</dependency>

<dependency>
    <groupId>org.apache.poi</groupId>
    <artifactId>poi</artifactId>
    <version>4.1.2</version>
</dependency>

<dependency>
    <groupId>org.apache.poi</groupId>
    <artifactId>poi-ooxml</artifactId>
    <version>4.1.2</version>
</dependency>

<dependency>
    <groupId>org.apache.poi</groupId>
    <artifactId>ooxml-schemas</artifactId>
    <version>1.4</version>
</dependency>

代码

package com.example.boottest.utils;

import cn.hutool.core.date.DateUtil;
import cn.hutool.poi.excel.ExcelUtil;
import cn.hutool.poi.excel.ExcelWriter;
import cn.hutool.poi.excel.StyleSet;
import lombok.SneakyThrows;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.streaming.SXSSFWorkbook;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;


/**
 * ExcelUtil.
 */
public class MyExcelUtil {

    /**
     * export by addHeaderAlias {@link cn.hutool.poi.excel.ExcelWriter#addHeaderAlias(String, String)}.
     *
     * @param response      response
     * @param title         标题
     * @param waterMark     水印
     * @param sheetNameList sheetName
     * @param listList      list
     * @param extList       addHeadAlias
     */
    public static void webExport(HttpServletResponse response, String title, String waterMark, List<String> sheetNameList, List<List<?>> listList, Consumer<List<ExcelWriter>> extList) {
        commonWebExport(response, title, waterMark, sheetNameList, listList, extList);
    }

    /**
     * export by annotation {@link cn.hutool.core.annotation.Alias}.
     *
     * @param response      response
     * @param title         标题
     * @param waterMark     水印
     * @param sheetNameList sheetName
     * @param listList      list
     */
    public static void webExport(HttpServletResponse response, String title, String waterMark, List<String> sheetNameList, List<List<?>> listList) {
        commonWebExport(response, title, waterMark, sheetNameList, listList, null);
    }

    public static void fileExport(String path, String title, String waterMark, List<String> sheetNameList, List<List<?>> listList, Consumer<List<ExcelWriter>> extList) {
        commonFileExport(path, title, waterMark, sheetNameList, listList, extList);
    }

    public static void fileExport(String path, String title, String waterMark, List<String> sheetNameList, List<List<?>> listList) {
        commonFileExport(path, title, waterMark, sheetNameList, listList, null);
    }


    private static void commonFileExport(String path, String title, String waterMark, List<String> sheetNameList, List<List<?>> listList, Consumer<List<ExcelWriter>> extList) {

        if (path.endsWith("\\")) {
            final StringBuilder builder = new StringBuilder(path);
            final int length = path.length();
            builder.delete(length - 1, length);
            path = builder.toString();
        }
        final String filename = String.format("%s\\%s_%s.xlsx", path, title, DateUtil.date().toString("yyyyMMddHHmmss"));
        try( ExcelWriter writer = excelWriter(waterMark, sheetNameList, listList, extList); FileOutputStream out = new FileOutputStream(filename);) {
            writer.flush(out, true);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @SneakyThrows
    private static void commonWebExport(HttpServletResponse response, String title, String waterMark, List<String> sheetNameList, List<List<?>> listList, Consumer<List<ExcelWriter>> extList) {
        final String filename = String.format("%s_%s.xlsx", title, DateUtil.date().toString("yyyyMMddHHmmss"));
        response.setContentType("application/vnd.ms-excel;charset=utf-8");
        response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(filename, "UTF-8"));
        try (ExcelWriter writer = excelWriter(waterMark, sheetNameList, listList, extList);ServletOutputStream out = response.getOutputStream()) {
            writer.flush(out, true);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private static ExcelWriter excelWriter(String waterMark, List<String> sheetNameList, List<List<?>> listList, Consumer<List<ExcelWriter>> extList) {
        ExcelWriter writer;
        // 防止内存溢出
        int count = 0;
        for (List<?> list : listList) {
            count += list.size();
        }
        if (count > 10000) {
            writer = ExcelUtil.getBigWriter();
        } else {
            writer = ExcelUtil.getWriter(true);
        }
        List<ExcelWriter> writers = new ArrayList<>();
        for (int i = 0; i < sheetNameList.size(); i++) {
            writer.setSheet(i);
            writer = writer.renameSheet(i, sheetNameList.get(i));
            writers.add(writer);
            // list
            writer.setOnlyAlias(true).write(listList.get(i), true);
        }
        // headAlias
        if (extList != null) {
            extList.accept(writers);
        }
        // 头部美化
        Font font = writer.createFont();
        font.setColor(IndexedColors.DARK_BLUE.index);
        font.setBold(true);
        font.setFontHeightInPoints((short) 13);
        font.setFontName("宋体");
        final StyleSet styleSet = writer.getStyleSet();
        styleSet.getHeadCellStyle().setFont(font);
        styleSet.getCellStyleForDate().setDataFormat(writer.getWorkbook().createDataFormat().getFormat("yyyy-MM-dd HH:mm:ss"));
        // 水平垂直
        final CellStyle cellStyle = styleSet.getCellStyle();
        cellStyle.setAlignment(HorizontalAlignment.CENTER);
        cellStyle.setVerticalAlignment(VerticalAlignment.CENTER);
        // 间隔美化
        final Sheet sheet = writer.getSheet();
        int columnCount = writer.getColumnCount();
        for (int i = 0; i < columnCount; i++) {
            sheet.autoSizeColumn(i);
        }
        // 添加水印
        final Workbook workbook = writer.getWorkbook();
        if (workbook instanceof XSSFWorkbook) {
            ExcelWatermarkUtil.putWatermarkToWorkbook((XSSFWorkbook) workbook, waterMark);
        }
        if (workbook instanceof SXSSFWorkbook) {
            ExcelWatermarkUtil.putWatermarkToWorkbook((SXSSFWorkbook) workbook, waterMark);
        }
        return writer;
    }
}

package com.example.boottest.utils;

import cn.hutool.core.util.ReflectUtil;
import lombok.SneakyThrows;
import org.apache.poi.openxml4j.opc.PackageRelationship;
import org.apache.poi.openxml4j.opc.TargetMode;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.xssf.streaming.SXSSFSheet;
import org.apache.poi.xssf.streaming.SXSSFWorkbook;
import org.apache.poi.xssf.usermodel.XSSFPictureData;
import org.apache.poi.xssf.usermodel.XSSFRelation;
import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;

import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.font.FontRenderContext;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import java.io.BufferedInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;


/**
 * 添加水印.
 */
public class ExcelWatermarkUtil {

    /**
     * 为XSSFWorkbook的每个Sheet添加水印
     *
     * @param workbook         待添加水印的workbook
     * @param watermarkContent 水印内容
     */
    public static void putWatermarkToWorkbook(XSSFWorkbook workbook, String watermarkContent) {
        try (ByteArrayOutputStream byteArrayOutputStream = createWaterMark(watermarkContent)) {
            int pictureIdx = workbook.addPicture(byteArrayOutputStream.toByteArray(), Workbook.PICTURE_TYPE_PNG);
            for (int i = 0; i < workbook.getNumberOfSheets(); i++) {
                XSSFSheet sheet = workbook.getSheetAt(i);
                PackageRelationship pr = sheet.getPackagePart().addRelationship(workbook.getAllPictures().get(pictureIdx).getPackagePart().getPartName(), TargetMode.INTERNAL, XSSFRelation.IMAGES.getRelation(), null);
                sheet.getCTWorksheet().addNewPicture().setId(pr.getId());
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }


    /**
     * 为SXSSFWorkbook的每个Sheet添加水印
     *
     * @param workbook         待添加水印的workbook
     * @param watermarkContent 水印内容
     */
    public static void putWatermarkToWorkbook(SXSSFWorkbook workbook, String watermarkContent) {
        try (ByteArrayOutputStream byteArrayOutputStream = createWaterMark(watermarkContent)) {
            int pictureIdx = workbook.addPicture(byteArrayOutputStream.toByteArray(), Workbook.PICTURE_TYPE_PNG);
            XSSFPictureData pictureData = (XSSFPictureData) workbook.getAllPictures().get(pictureIdx);

            for (int i = 0; i < workbook.getNumberOfSheets(); i++) {
                SXSSFSheet sheet = workbook.getSheetAt(i);
                XSSFSheet shReflect = (XSSFSheet) ReflectUtil.getFieldValue(sheet, "_sh");
                PackageRelationship pr = shReflect.getPackagePart().addRelationship(pictureData.getPackagePart().getPartName(), TargetMode.INTERNAL, XSSFRelation.IMAGES.getRelation(), null);
                shReflect.getCTWorksheet().addNewPicture().setId(pr.getId());
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * @param content 水印内容
     * @return byteArrayOutputStream    水印图片流,使用后请关闭
     */
    @SneakyThrows
    private static ByteArrayOutputStream createWaterMark(String content)  {
        BufferedImage waterMarkImage = new BufferedImage(WatermarkParam.WIDTH, WatermarkParam.HEIGHT, BufferedImage.TYPE_INT_RGB);
        Graphics2D graphics2d = waterMarkImage.createGraphics();
        waterMarkImage = graphics2d.getDeviceConfiguration().createCompatibleImage(WatermarkParam.WIDTH, WatermarkParam.HEIGHT, Transparency.TRANSLUCENT);
        graphics2d.dispose();
        graphics2d = waterMarkImage.createGraphics();
        graphics2d.setColor(WatermarkParam.COLOR);
        graphics2d.setStroke(new BasicStroke(1));
        Font font = createFont();
        graphics2d.setFont(font);
        graphics2d.rotate(WatermarkParam.THETA, (double) waterMarkImage.getWidth() / 2, (double) waterMarkImage.getHeight() / 2);
        FontRenderContext context = graphics2d.getFontRenderContext();
        Rectangle2D bounds = font.getStringBounds(content, context);
        double x = (WatermarkParam.WIDTH - bounds.getWidth()) / 2;
        double y = (WatermarkParam.HEIGHT - bounds.getHeight()) / 2;
        double ascent = -bounds.getY();
        double baseY = y + ascent;
        graphics2d.drawString(content, (int) x, (int) baseY);
        graphics2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER));
        graphics2d.dispose();
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        ImageIO.write(waterMarkImage, WatermarkParam.FORMAT, byteArrayOutputStream);
        return byteArrayOutputStream;
    }


    private static Font createFont() throws IOException {
        Resource resource = new ClassPathResource("fonts/msyh.ttf");
        int fontStyle = Font.PLAIN;
        try (BufferedInputStream bs = new BufferedInputStream(resource.getInputStream())) {
            Font font = Font.createFont(fontStyle, bs);
            font = font.deriveFont(fontStyle, 30);
            return font;
        } catch (FontFormatException e) {
            throw new RuntimeException(e);
        }

    }

    public static final class WatermarkParam {
        /**
         * 水印图片尺寸
         */
        public final static int WIDTH = 300;
        public final static int HEIGHT = 250;

        /**
         * 水印颜色,不透明度
         */
        public final static Color COLOR = new Color(0,0,0,20);

        /**
         * 水印旋转角度
         */
        public final static double THETA = -0.5;

        /**
         * 水印图片流格式
         */
        public final static String FORMAT = "png";
    }
}