java解決poi導出excel文字水印,導出excel不可操作問題


首先需求是用戶提出導出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

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM