java解决poi导出excel文字水印,导出excel不可操作问题

时间:2024-03-10 12:52:47

首先需求是用户提出导出excel数据需使用水印备注其用途;

其实就是在导出excel的同时带有自定义文字水印的导出。

那么我们首先想到的肯定是以一个什么样的思路去解决该问题,首先查找poi导出excel有没有相关技术可以直接导出文字水印,可想而知我写了这篇博客,当然是没有一步走成的方法

那么我们开始换一种思路,大家都知道图片可以添加文字水印和图片水印,那么既然图片可以添加文字水印,可能就可以想到excel可以导出图片的功能。那么我们可以先创建一个透明色的图片,然后添加文字水印,最后添加到excel中导出,结束,那么我们直接来看实现

首先看一下如何生成有文字水印的透明色图片

/**
     * 生成背景透明的 文字水印,文字位于透明区域正*,可设置旋转角度
     *
     * @param width 生成图片宽度
     * @param heigth 生成图片高度
     * @param text 水印文字
     * @param color 颜色对象
     * @param font awt字体
     * @param degree 水印文字旋转角度
     * @param alpha 水印不透明度0f-1.0f
     */
    public static BufferedImage waterMarkByText(int width, int heigth, String text, Color color,
                                                Font font, Double degree, float alpha) {
        BufferedImage buffImg = new BufferedImage(width, heigth, BufferedImage.TYPE_INT_RGB);
        /**2、得到画笔对象*/
        Graphics2D g2d = buffImg.createGraphics();
        // ----------  增加下面的代码使得背景透明  -----------------
        buffImg = g2d.getDeviceConfiguration().createCompatibleImage(width, heigth, Transparency.TRANSLUCENT);
        g2d.dispose();
        g2d = buffImg.createGraphics();
        // ----------  背景透明代码结束  -----------------

        // 设置对线段的锯齿状边缘处理
        g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
        // 设置水印旋转
        if (null != degree) {
            //注意rotate函数参数theta,为弧度制,故需用Math.toRadians转换一下
            //以矩形区域*为圆心旋转
            g2d.rotate(Math.toRadians(degree), (double) buffImg.getWidth() / 2, (double) buffImg.getHeight() / 2);
        }
        // 设置颜色
        g2d.setColor(color);
        // 设置 Font
        g2d.setFont(font);
        //设置透明度:1.0f为透明度 ,值从0-1.0,依次变得不透明
        g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, alpha));
        //计算绘图偏移x、y,使得使得水印文字在图片中居中
        //这里需要理解x、y坐标是基于Graphics2D.rotate过后的坐标系
        int x = -width / 3;
        int y = -heigth / 2;
        // 字体长度
        int markWidth = font.getSize() * getTextLength (text);
        // 字体高度
        int markHeight = font.getSize();
        // 循环添加水印
        while (x < width * 1.5) {
            y = -heigth / 2;
            while (y < heigth * 1.5) {
                g2d.drawString (text, x, y);
                y += markHeight + 200;
            }
            x += markWidth + 200;
        }
        //取绘制的字串宽度、高度中间点进行偏移,使得文字在图片坐标中居中
        //释放资源
        g2d.dispose();
        return buffImg;

然后在原来的导出文件方法中添加用来获取已经添加文字的透明色图片

 /**
     * 生成导出文件
     * @param sheetName
     * @param dataItems
     * @param columnKeys
     * @param columnNames
     * @throws IOException
     */
    public static void initExportFileToWb(HSSFWorkbook wb,String sheetName,List<Map<String,Object>> dataItems,List<String> columnKeys,List<String> columnNames,String waterText) throws IOException {
        HSSFSheet sheet = wb.createSheet(sheetName);
        // 设置安全性密码
        sheet.protectSheet(UUID.randomUUID().toString());
        HSSFRow row = sheet.createRow(0);
        row.setHeight((short) 500);
        // 学生信息excel
        // 初始化头部
        initHeader(wb,sheet,columnNames);
        // 初始化数据
        initData(sheet, dataItems, columnKeys,columnNames);
     // 根据导出列数计算图片的宽度  153和17是写定的,大家看情况
        int width = columnNames.size()*153;
     // 根据导出数据计算图片的高度
        int height = (dataItems.size()+2)*17;
     // 获取有文字水印的透明色图片
        BufferedImage water = CommonUtils.getWaterImage(width,height,waterText);
     导入图片
        putWaterRemarkToExcel(wb,sheet,water,20,40);
    }

最后就是导出excel时插入图片

/**
     * 为Excel打上水印工具函数
     * 请自行确保参数值,以保证水印图片之间不会覆盖。
     * 在计算水印的位置的时候,并没有考虑到单元格合并的情况,请注意
     * @param wb       Excel Workbook
     * @param sheet    需要打水印的Excel
     * @param waterRemarkPath  水印地址,classPath,目前只支持png格式的图片,
     *                         因为非png格式的图片打到Excel上后可能会有图片变红的问题,且不容易做出透明效果。
     *                         同时请注意传入的地址格式,应该为类似:"\\excelTemplate\\test.png"
     * @param waterRemarkWidth 水印图片宽度为多少列
     * @param waterRemarkHeight 水印图片高度为多少行
     * @throws IOException
     */
    public static void putWaterRemarkToExcel(HSSFWorkbook wb,HSSFSheet sheet, BufferedImage waterRemarkPath,
                                             int waterRemarkWidth, int waterRemarkHeight) throws IOException{

        //加载图片
        ByteArrayOutputStream byteArrayOut = new ByteArrayOutputStream();
        if(null == waterRemarkPath) {
            throw new RuntimeException("向Excel上面打印水印,读取水印图片失败(2)。");
        }
        ImageIO.write(waterRemarkPath,"png",byteArrayOut);
        //开始打水印
        Drawing drawing = sheet.createDrawingPatriarch();
        /*
         * 参数定义:
         * 第一个参数是(x轴的开始节点);
         * 第二个参数是(是y轴的开始节点);
         * 第三个参数是(是x轴的结束节点);
         * 第四个参数是(是y轴的结束节点);
         * 第五个参数是(是从Excel的第几列开始插入图片,从0开始计数);
         * 第六个参数是(是从excel的第几行开始插入图片,从0开始计数);
         * 第七个参数是(图片宽度,共多少列);
         * 第8个参数是(图片高度,共多少行);
         */
        ClientAnchor anchor = drawing.createAnchor(0, 0, 255, 255, 0, 0, waterRemarkWidth, waterRemarkHeight);
        Picture pic = drawing.createPicture(anchor, wb.addPicture(byteArrayOut.toByteArray(), Workbook.PICTURE_TYPE_PNG));
        pic.resize();


    }

完成后查看导出excel结果

 

 最后既然是水印,当然该文件不可操作,可通过设置查看密码进行限制,限制如图

// 设置安全性密码,随机的谁也不知道
sheet.protectSheet(UUID.randomUUID().toString());

 最后:如果上传到服务器水印文字无法正常显示

复制C盘Windows/Fonts下字体到项目,然后使用该字体,获取到改字体创建后要设置字体大小

font = Font.createFont(Font.TRUETYPE_FONT, new FileInputStream(request.getSession().getServletContext().getRealPath("/template/simsun.ttc")));
font = font.deriveFont(Font.ROMAN_BASELINE,32);

 完美解决!第一次写博客有问题大家多多指教,谢谢!

 完成该需求参考了:

https://www.cnblogs.com/wanggangblog/p/6767481.html

https://www.cnblogs.com/wzluo09/p/9669989.html