Java使用EasyExcel导出添加水印

当我们在Excel导出中添加水印时,我们可以采用一种方法,先将水印文字生成为图片,然后将这个图片设置为Excel的背景。这样的做法涉及到使用Java的Graphics2D来处理图片。但是需要注意的是,这种方式存在一个问题,就是在将导出的Excel再转换为PDF格式时,水印会消失。

步骤

  1. 使用Java的Graphics2D处理水印文字,将其生成为图片。
  2. 将生成的水印图片作为背景图片嵌入到Excel中。
  3. 执行Excel导出操作,确保水印在导出的Excel文件中显示。
  4. 注意,在将Excel转换为PDF时,会丢失水印的问题,这部分需要后续的解决方案。

一、引入jar包

	
	<dependency>
		<groupId>com.alibabagroupId>
		<artifactId>easyexcelartifactId>
		<version>3.3.2version>
	dependency>
	
	<dependency>
		<groupId>org.apache.poigroupId>
		<artifactId>ooxml-schemasartifactId>
		<version>1.4version>
	dependency>
	
	<dependency>
		<groupId>cn.hutoolgroupId>
		<artifactId>hutool-allartifactId>
		<version>5.8.20version>
	dependency>

二、Graphics2D的使用

下面将用一个简单的案例来介绍Graphics2D的使用。

读取一张图片,在图片中添加一段文字,然后输出一段文字,最后再输出添加文字的图片。

Java使用EasyExcel导出添加水印_第1张图片

@SpringBootTest
public class PictureTest {
    @Test
    void contextLoads() {
        try {
            InputStream inputStream = new FileInputStream("D:\\images/cat.jpg");
            BufferedImage bufferedImage = ImageIO.read(inputStream);
            Graphics2D graphics2D = bufferedImage.createGraphics();
            Font font = new Font("黑体", Font.BOLD, 40);
            graphics2D.setFont(font);
            graphics2D.drawString("我是一只可爱的小猫", 800, 800);
            ImageIO.write(bufferedImage, "jpg", new FileOutputStream("D:\\images/catOutPut.png"));
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
}

我们有一个位于D盘的images文件夹,其中包含了一些照片。我们的目标是读取这些照片,然后将它们加工后再输出到D盘的images文件夹中。在处理照片的过程中,我们需要实现在图片中添加文字的功能。为了实现这一目标,我们可以通过获取文件流,然后使用这个文件流创建一个Graphics2D对象。通过Graphics2D对象,我们可以在图片上绘制文本或其他图形,从而实现在图片中添加文字。

步骤

  1. 读取位于D盘images文件夹的照片。
  2. 使用文件流创建Graphics2D对象,实现在图片中添加文字。
  3. 输出加工后的图片到D盘的images文件夹中,保存为新的文件。
    Java使用EasyExcel导出添加水印_第2张图片

在导出Excel文件是添加水印我们也是利用这样的方法。

三、具体实现

步骤

  • 使用了Apache POI库来操作Excel文件,通过XSSFWorkbook、XSSFSheet等类来表示Excel的工作簿和工作表。
  • 通过workbook.addPicture()方法将水印图片添加到工作簿,并获取图片索引。
  • 然后通过遍历工作簿中的所有工作表,使用xssfSheet.getPackagePart().addRelationship()方法建立工作表与图片的关联关系。
  • 最后通过xssfSheet.getCTWorksheet().addNewPicture().setId()方法将水印添加到每个工作表的底层XML表示中。

编写水印的配置类

/**
 * 水印配置类
 */
@Data
public class WaterMark {
    /**
     * 水印内容
     */
    private String content = "";

    /**
     * 画笔颜色. 格式为"#RRGGBB",eg: "#C5CBCF"
     */
    private String color = "#C5CBCF";

    /**
     * 字体样式
     */
    private Font font = new Font("Microsoft YaHei", Font.BOLD, 40);

    /**
     * 水印宽度
     */
    private int width = 300;

    /**
     * 水印高度
     */
    private int height = 100;

    /**
     * 水平倾斜度
     */
    private double shearX = 0.1;

    /**
     * 垂直倾斜度
     */
    private double shearY = -0.26;

    /**
     * 字体的y轴位置
     */
    private int yAxis = 50;

    // Getter and Setter methods...
}

编写Handler

多个工作表导出

public class CustomWaterMarkHandler implements SheetWriteHandler {

    private final WaterMark watermark;

    public CustomWaterMarkHandler(WaterMark watermark) {
        this.watermark = watermark;
    }

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

    }

    @Override
    public void afterSheetCreate(WriteWorkbookHolder writeWorkbookHolder, WriteSheetHolder writeSheetHolder) {
        try {
            BufferedImage bufferedImage = createWatermarkImage();
            setWaterMarkToExcel((XSSFWorkbook) writeWorkbookHolder.getWorkbook(), bufferedImage);
        } catch (Exception e) {
            throw new RuntimeException("添加水印出错");
        }
    }

    private BufferedImage createWatermarkImage() {
        final Font font = watermark.getFont();
        final int width = watermark.getWidth();
        final int height = watermark.getHeight();

        String[] textArray = watermark.getContent().split(",");
        BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
        // 背景透明 开始
        Graphics2D g = image.createGraphics();
        image = g.getDeviceConfiguration().createCompatibleImage(width, height, Transparency.TRANSLUCENT);
        g.dispose();
        // 背景透明 结束
        g = image.createGraphics();
        // 设定画笔颜色
        g.setColor(new Color(Integer.parseInt(watermark.getColor().substring(1), 16)));
        // 设置画笔字体
        g.setFont(font);
        // 设定倾斜度
        g.shear(watermark.getShearX(), watermark.getShearY());

        // 设置字体平滑
        g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);

        int y = watermark.getYAxis();
        for (String s : textArray) {
            // 从画框的y轴开始画字符串.假设电脑屏幕中心为0,y轴为正数则在下方
            g.drawString(s, 0, y);
            y = y + font.getSize();
        }

        // 释放画笔
        g.dispose();
        return image;
    }

    private void setWaterMarkToExcel(XSSFWorkbook workbook, BufferedImage bfi) {
        //将图片添加到工作簿
        int pictureIdx = workbook.addPicture(ImgUtil.toBytes(bfi, ImgUtil.IMAGE_TYPE_PNG), Workbook.PICTURE_TYPE_PNG);
        //建立 sheet 和 图片 的关联关系
        XSSFPictureData xssfPictureData = workbook.getAllPictures().get(pictureIdx);
        for (int i = 0; i < workbook.getNumberOfSheets(); i++) {
            XSSFSheet xssfSheet = workbook.getSheetAt(i);
            PackagePartName packagePartName = xssfPictureData.getPackagePart().getPartName();
            PackageRelationship packageRelationship = xssfSheet.getPackagePart()
                    .addRelationship(packagePartName, TargetMode.INTERNAL, XSSFRelation.IMAGES.getRelation(), null);
            //添加水印到工作表
            xssfSheet.getCTWorksheet().addNewPicture().setId(packageRelationship.getId());
        }
    }
}

单个工作表导出

@RequiredArgsConstructor
public class WaterMarkHandler implements SheetWriteHandler {
    private final WaterMark watermark;

    private BufferedImage createWatermarkImage(WaterMark watermark) {
        final Font font = this.watermark.getFont();
        final int width = this.watermark.getWidth();
        final int height = this.watermark.getHeight();

        String[] textArray = this.watermark.getContent().split(",");
        BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
        // 背景透明 开始
        Graphics2D g = image.createGraphics();
        image = g.getDeviceConfiguration().createCompatibleImage(width, height, Transparency.TRANSLUCENT);
        g.dispose();
        // 背景透明 结束
        g = image.createGraphics();
        // 设定画笔颜色
        g.setColor(new Color(Integer.parseInt(this.watermark.getColor().substring(1), 16)));
        // 设置画笔字体
        g.setFont(font);
        // 设定倾斜度
        g.shear(this.watermark.getShearX(), this.watermark.getShearY());

        // 设置字体平滑
        g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);

        int y = this.watermark.getYAxis();
        for (String s : textArray) {
            // 从画框的y轴开始画字符串.假设电脑屏幕中心为0,y轴为正数则在下方
            g.drawString(s, 0, y);
            y = y + font.getSize();
        }

        // 释放画笔
        g.dispose();
        return image;
    }


    /**
     * 为Excel打上水印工具函数
     *
     * @param sheet excel sheet
     * @param bytes 水印图片字节数组
     */
    public static void putWaterRemarkToExcel(XSSFSheet sheet, BufferedImage bufferedImage) {
        //add relation from sheet to the picture data
        XSSFWorkbook workbook = sheet.getWorkbook();
        int pictureIdx = workbook.addPicture(ImgUtil.toBytes(bufferedImage, ImgUtil.IMAGE_TYPE_PNG), 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) {
    }

    @SneakyThrows
    @Override
    public void afterSheetCreate(WriteWorkbookHolder writeWorkbookHolder, WriteSheetHolder writeSheetHolder) {
        BufferedImage waterMark = createWatermarkImage(watermark);
        XSSFSheet sheet = (XSSFSheet) writeSheetHolder.getSheet();
        putWaterRemarkToExcel(sheet, waterMark);
    }
}

测试

@Getter
@Setter
@EqualsAndHashCode
public class DemoData {
    @ExcelProperty("字符串标题")
    private String string;
    @ExcelProperty(value = "日期标题",converter = DateConverter.class)
    private Date date;
    @ExcelProperty("数字标题")
    private Double doubleData;
    /**
     * 忽略这个字段
     */
    @ExcelIgnore
    private String ignore;
}
@SpringBootTest
@Slf4j
public class WaterMarkTest {
    @Test
    void WaterMarkReader() {
        WaterMark watermark = new WaterMark();
        watermark.setContent("我是sveinn,我热爱Java");
        watermark.setWidth(600);
        watermark.setHeight(400);
        watermark.setYAxis(200);
		// 注意文件是要.xlsx
        String fileName ="D:/export/waterMark "+ ".xlsx";

//导出
        EasyExcel.write(fileName, DemoData.class)
                .inMemory(true)
                .sheet("sheet1")
                .registerWriteHandler(new CustomWaterMarkHandler(watermark))
                .doWrite(data());
    }
    private List<DemoData> data() {
        List<DemoData> list = new ArrayList<DemoData>();
        for (int i = 0; i < 10; i++) {
            DemoData data = new DemoData();
            data.setString("字符串" + i);
            data.setDate(new Date());
            data.setDoubleData(0.56);
            list.add(data);
        }
        return list;
    }
}

具体想要导出什么样的水印可以根据水印的配置类配置自己想要的样式,如果想要更好看的水印可以研究以下Graphics2D的使用。

Java使用EasyExcel导出添加水印_第3张图片

三、需要注意的问题

  • POI中的XSSFWorkbook对象,也就是对应Excel2007的版本,扩展名是.xlsx
  • 导出要用内存模式inMemory(true),会比较耗内存。

本文参考easyexcel实现导出添加文字水印 - 雨叶微枫 - 博客园

你可能感兴趣的:(java,导出,easyexcel)