自定义拦截器

@RequiredArgsConstructor
public class WaterMarkHandler implements SheetWriteHandler {

    private final String WATER_MARK;

    public static ByteArrayOutputStream createWaterMark(String content) throws IOException {
        int width = 200;
        int height = 150;
        BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);// 获取bufferedImage对象
        String fontType = "微软雅黑";
        int fontStyle = Font.BOLD;
        int fontSize = 20;
        Font font = new Font(fontType, fontStyle, fontSize);
        Graphics2D g2d = image.createGraphics(); // 获取Graphics2d对象
        image = g2d.getDeviceConfiguration().createCompatibleImage(width, height, Transparency.TRANSLUCENT);
        g2d.dispose();
        g2d = image.createGraphics();
        g2d.setColor(new Color(0, 0, 0, 80)); //设置字体颜色和透明度,最后一个参数为透明度
        g2d.setStroke(new BasicStroke(1)); // 设置字体
        g2d.setFont(font); // 设置字体类型  加粗 大小
        g2d.rotate(-0.5, (double) image.getWidth() / 2, (double) image.getHeight() / 2);//设置倾斜度
        FontRenderContext context = g2d.getFontRenderContext();
        Rectangle2D bounds = font.getStringBounds(content, context);
        double x = (width - bounds.getWidth()) / 2;
        double y = (height - bounds.getHeight()) / 2;
        double ascent = -bounds.getY();
        double baseY = y + ascent;
        // 写入水印文字原定高度过小,所以累计写水印,增加高度
        g2d.drawString(content, (int) x, (int) baseY);
        // 设置透明度
        g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER));
        // 释放对象
        g2d.dispose();
        ByteArrayOutputStream os = new ByteArrayOutputStream();
        ImageIO.write(image, "png", os);
        return os;
    }

    /**
     * 为Excel打上水印工具函数
     *
     * @param sheet excel sheet
     * @param bytes 水印图片字节数组
     */
    public static void putWaterRemarkToExcel(XSSFSheet sheet, byte[] bytes) {
        //add relation from sheet to the picture data
        XSSFWorkbook workbook = sheet.getWorkbook();

        int pictureIdx = workbook.addPicture(bytes, Workbook.PICTURE_TYPE_PNG);
        String rID = sheet.addRelation(null, XSSFRelation.IMAGES, workbook.getAllPictures().get(pictureIdx))
                .getRelationship().getId();
        //set background picture to sheet
        sheet.getCTWorksheet().addNewPicture().setId(rID);
    }

    @Override
    public void beforeSheetCreate(WriteWorkbookHolder writeWorkbookHolder, WriteSheetHolder writeSheetHolder) {

    }

    @Override
    public void afterSheetCreate(WriteWorkbookHolder writeWorkbookHolder, WriteSheetHolder writeSheetHolder) {
        try (ByteArrayOutputStream waterMark = createWaterMark(WATER_MARK)){
            XSSFSheet sheet = (XSSFSheet) writeSheetHolder.getSheet();
            putWaterRemarkToExcel(sheet, waterMark.toByteArray());
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

控制层

@RequestMapping("/waterMark")
public void waterMark(HttpServletResponse response) throws IOException {
    service.waterMark(response);
}

实现层

    @Override
    public void waterMark(HttpServletResponse response) throws IOException {
        ServletOutputStream outputStream = EasyExcelUtil.getOutputStream(response);
        ExcelWriter writer = EasyExcel.write(outputStream).inMemory(true).build();
        WriteSheet sheet = EasyExcel
                .writerSheet(0,"水印测试")
                .registerWriteHandler(new WaterMarkHandler("赵今麦Angel"))
                .sheetName("水印测试sheet")
                .build();
        writer.write(new ArrayList(),sheet);
        writer.finish();
    }

注意: Excel添加水印,只支持XSSFWorkbook,其余类别:SXSSFWorkbook、SXSSFWorkbook不支持。

EasyExcel使用时需要设置inMemory(true)参数,否者默认使用的是SXSSFWorkbook,会报错

实现效果

水印

水印乱码问题或文字不显示

在我的使用过程中,本地测试导出水印正常,但是在服务器上面导出水印不显示中文,查找原因发现是缺少中文字体,解决办法如下:

1.下载中文字体, 微软雅黑

2.将下载的ttf字体文件放在resource目录下

image-20220429134740070

3.pom文件

<build>
    <resources>
        <resource>
            <directory>src/main/resources</directory>
            <filtering>true</filtering>
            <includes>
                <include>**/*.properties</include>
                <include>**/*.xml</include>
                <include>**/*.yml</include>
            </includes>
            <excludes>
                <exclude>**/*.ttf</exclude>
            </excludes>
        </resource>
        <resource>
            <directory>src/main/resources</directory>
            <!--一定要在filtering=false中加载字体文件,否则maven打包破坏字体文件-->
            <filtering>false</filtering>
            <includes>
                <include>**/*.ttf</include>
            </includes>
        </resource>
    </resources>
</build>

4.修改创建水印的代码

public static ByteArrayOutputStream createWaterMark(String content) throws IOException, FontFormatException {
    int width = 200;
    int height = 150;
    // 添加中文字体 加载下载的中文字体,这样就不会出现中文乱码或中文丢失的情况
    ClassPathResource resource = new ClassPathResource("font/msyh.ttf");
    InputStream fi = resource.getInputStream();
    BufferedInputStream fb = new BufferedInputStream(fi);
    // String fontType = "微软雅黑";
    int fontStyle = Font.PLAIN;
    int fontSize = 20;
    // Font font = new Font(fontType, fontStyle, fontSize);
    Font font = Font.createFont(fontStyle,fb);
    font = font.deriveFont(fontStyle,fontSize);
    BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);// 获取bufferedImage对象
    // 获取Graphics2d对象
    Graphics2D g2d = image.createGraphics();
    image = g2d.getDeviceConfiguration().createCompatibleImage(width, height, Transparency.TRANSLUCENT);
    g2d.dispose();
    g2d = image.createGraphics();
    //设置字体颜色和透明度,最后一个参数为透明度
    g2d.setColor(new Color(0, 0, 0, 80));
    // 设置字体
    g2d.setStroke(new BasicStroke(1));
    // 设置字体类型  加粗 大小
    g2d.setFont(font);
    g2d.rotate(-0.5, (double) image.getWidth() / 2, (double) image.getHeight() / 2);//设置倾斜度
    FontRenderContext context = g2d.getFontRenderContext();
    Rectangle2D bounds = font.getStringBounds(content, context);
    double x = (width - bounds.getWidth()) / 2;
    double y = (height - bounds.getHeight()) / 2;
    double ascent = -bounds.getY();
    double baseY = y + ascent;
    // 写入水印文字原定高度过小,所以累计写水印,增加高度
    g2d.drawString(content, (int) x, (int) baseY);
    // 设置透明度
    g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER));
    // 释放对象
    g2d.dispose();
    ByteArrayOutputStream os = new ByteArrayOutputStream();
    ImageIO.write(image, "png", os);
    return os;
}
最后修改:2022 年 04 月 29 日
请博主喝杯咖啡~~~