zoukankan      html  css  js  c++  java
  • 【转】easypoi 导出excel 不需要本地模版,本地图片 添加水印,加密的方式

    原文链接:https://my.oschina.net/u/4048764/blog/3740809

    有个需求,需要导出excel的时候,添加水印,但是网上搜到的很多都是 根据模版或者根据本地图片添加水印,

    和我遇到的情况不太一样,我这边是直接根据查出的数据导出excel然后通过流传回前端.没有用到本地的文件.

    这里感谢这篇文章.我也只是根据这篇文章改了一点东西罢了.

    https://www.jianshu.com/p/5ebf2217f0be

    下面贴代码

    我用的是easy-poi 4.1.3

    生成水印的工具类,

    其实原理是生成背景透明的图片插入到excel中

    private static final Logger logger = LoggerFactory.getLogger(ExcelWaterRemarkUtils.class);
    
    // 宽度
    private static final int WIDTH = 360;
    // 高度
    private static final int HEIGHT = 140;
    // 水印透明度
    private static float alpha = 1.0f;
    // 水印横向位置
    private static int positionWidth = 80;
    // 水印纵向位置
    private static int positionHeight = 30;
    // 水印文字字体
    private static Font font = new Font("宋体", Font.PLAIN, 40);
    // 水印文字颜色
    private static Color color = new Color(230, 230, 230);
    /**
         * 创建背景透明图片
    *
         * @param os
    * @param text
    * @param fontPath
    * @throws IOException
         */
    public static BufferedImage createTransparentImage(OutputStream os, String text,String fontPath) throws IOException {
            // 创建空白图片
    BufferedImage image = new BufferedImage(
                    WIDTH, HEIGHT, BufferedImage.TYPE_INT_ARGB);
            // 获取图片画笔
    Graphics2D g = image.createGraphics();
            // 设置背景透明度
    image = g.getDeviceConfiguration().createCompatibleImage(WIDTH, HEIGHT, Transparency.TRANSLUCENT);
            g.dispose();
            g = image.createGraphics();
            // 设置对线段的锯齿状边缘处理
    // g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
    g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
            // 文字处理
    AttributedString ats = new AttributedString(text);
    
            Font  font1 = SystemLoadFont.styleFont(fontPath,Font.PLAIN,30);
            ats.addAttribute(TextAttribute.FONT, font1, 0, text.length());
            AttributedCharacterIterator iter = ats.getIterator();
            // 水印旋转
    g.rotate(Math.toRadians(-15), (double) image.getWidth() / 2, (double) image.getHeight() / 2);
            // 设置水印文字颜色
    g.setColor(color);
            // 设置水印文字Font
    g.setFont(font1);
    //        alpha = 0.3f;
            // 设置水印文字透明度
    g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_ATOP, alpha));
            // 设置水印文字透明度结束
    g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER));
            // 水印位置
    g.drawString(iter, positionHeight, positionWidth);
            // 释放资源
    g.dispose();
            return image;
        }
    /*
     * Excel打上水印工具函数 请自行确保参数值,以保证水印图片之间不会覆盖。 在计算水印的位置的时候,并没有考虑到单元格合并的情况,请注意
     *
     * @param wb
     *            Excel Workbook
     * @param sheet
     *            需要打水印的Excel
     * @param waterRemarkPath
     *            水印地址,classPath,目前只支持png格式的图片,
     *            因为非png格式的图片打到Excel上后可能会有图片变红的问题,且不容易做出透明效果。
     *            同时请注意传入的地址格式,应该为类似:"\excelTemplate\test.png"
     * @param startXCol
     *            水印起始列
     * @param startYRow
     *            水印起始行
     * @param betweenXCol
     *            水印横向之间间隔多少列
     * @param betweenYRow
     *            水印纵向之间间隔多少行
     * @param XCount
     *            横向共有水印多少个
     * @param YCount
     *            纵向共有水印多少个
     * @param waterRemarkWidth
     *            水印图片宽度为多少列
     * @param waterRemarkHeight
     *            水印图片高度为多少行
     * @throws IOException
     */
    public static void putWaterRemarkToExcel(Workbook wb, Sheet sheet, BufferedImage bufferImg, int startXCol,
                                             int startYRow, int betweenXCol, int betweenYRow, int XCount, int YCount, int waterRemarkWidth,
                                             int waterRemarkHeight) throws IOException {
        // 加载图片
    ByteArrayOutputStream byteArrayOut = new ByteArrayOutputStream();
        if (null == bufferImg) {
            throw new RuntimeException("Excel上面打印水印,读取水印图片失败(2)");
        }
        ImageIO.write(bufferImg, "png", byteArrayOut);
        // 开始打水印
    Drawing drawing = sheet.createDrawingPatriarch();
        // 按照共需打印多少行水印进行循环
    for (int yCount = 0; yCount < YCount; yCount++) {
            // 按照每行需要打印多少个水印进行循环
    for (int xCount = 0; xCount < XCount; xCount++) {
                // 创建水印图片位置
    int xIndexInteger = startXCol + (xCount * waterRemarkWidth) + (xCount * betweenXCol);
                int yIndexInteger = startYRow + (yCount * waterRemarkHeight) + (yCount * betweenYRow);
                /*
                 * 参数定义: 第一个参数是(x轴的开始节点); 第二个参数是(是y轴的开始节点); 第三个参数是(是x轴的结束节点);
                 * 第四个参数是(是y轴的结束节点); 第五个参数是(是从Excel的第几列开始插入图片,从0开始计数);
                 * 第六个参数是(是从excel的第几行开始插入图片,从0开始计数); 第七个参数是(图片宽度,共多少列);
                 * 8个参数是(图片高度,共多少行);
                 */
    ClientAnchor anchor = drawing.createAnchor(0, 0, 0, 0, xIndexInteger,
                        yIndexInteger, xIndexInteger + waterRemarkWidth, yIndexInteger + waterRemarkHeight);
    
                Picture pic = drawing.createPicture(anchor,
                        wb.addPicture(byteArrayOut.toByteArray(), Workbook.PICTURE_TYPE_PNG));
                pic.resize();
            }
        }
    }
    //获取文本长度
    private static int getTextLength(String text) {
        int length = text.length();//获取所有文本的长度
    for (int i = 0; i < text.length(); i++) {
            String s = String.valueOf(text.charAt(i));//获取指定的字符
    if (s.getBytes().length > 1) {//字节长度大于1,说明是中文,那么需要延长文本长度
    length++;
            }
        }
        //计算总共有多少个字节,也就是有多少个字
    length = (length % 2 == 0) ? length / 2 : length / 2 + 1;
        return length;
    }

    实现流程

    1.生成excel,使用easyPoi自带的工具类生成的.

    Workbook wb = ExcelExportUtil.exportExcel(new ExportParams(title, title, ExcelType.HSSF),Apportion.class ,result);

    2.导出到前端的方法

    export(response,wb,file,text);//file 文件名,text 水印文字
    private void export(HttpServletResponse response, Workbook workbook, String fileName,String text) throws Exception {
        // 重置响应对象
    response.reset();
        response.setHeader("Content-Disposition",
                "attachment;filename=" + URLEncoder.encode(fileName, "UTF-8"));
        response.setHeader("content-Type", "application/vnd.ms-excel");
        response.setCharacterEncoding("UTF-8");
        response.setHeader("Pragma", "no-cache");
        response.setHeader("Cache-Control", "no-cache");
        response.setDateHeader("Expires", 0);
        // 写出数据输出流到页面
    try {
            OutputStream output = response.getOutputStream();
            BufferedOutputStream bufferedOutPut = new BufferedOutputStream(output);
            byte[] bytes = text.getBytes();
            text = new String(bytes,StandardCharsets.UTF_8);
            BufferedImage bufferedImage = ExcelWaterRemarkUtils.createTransparentImage(bufferedOutPut,text,fontPath);//fontPath 字体文件所在路径
            int sheets = workbook.getNumberOfSheets();
            //循环sheet给每个sheet添加水印
    for (int i = 0; i < sheets; i++) {
                Sheet sheet = workbook.getSheetAt(i);
                //获取excel实际所占行
    int row = sheet.getFirstRowNum() + sheet.getLastRowNum();
                //获取excel实际所占列
    int cell = sheet.getRow(sheet.getFirstRowNum()).getLastCellNum() + 1;
                for(int n = 0;n < cell ;n++){
                    sheet.autoSizeColumn((short)n,false);
                }
                //根据行与列计算实际所需多少水印
    ExcelWaterRemarkUtils.putWaterRemarkToExcel(workbook, sheet, bufferedImage, 0, 0, 5, 5, cell / 5 + 1, row / 5 + 1, 0, 0);
                //excel加密只读
    sheet.protectSheet(UUID.randomUUID().toString());
            }
            workbook.write(bufferedOutPut);
            bufferedOutPut.flush();
            bufferedOutPut.close();
            output.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    需要注意,如果生产环境部署在docker 或者是 是linux服务器,很有可能会引起中文水印乱码 成了方块的情况,这时候你就需要自己导入字体放到项目路径.然后读取.下面是字体相关的工具类

    public class SystemLoadFont {
        /**
         * 本地读取方法
    * @param path 文件路径
    * @param style 字体样式
    * @param fontSize 字体大小
    * @return
    */
    public static java.awt.Font styleFont(String path,int style,float fontSize) {
           
            return SystemLoadFont.loadStyleFont(path,style,fontSize);
        }
        /**
         *
         * @param fontFileName 外部字体名
    * @param style 字体样式
    * @param fontSize 字体大小
    * @return
    */
    public static Font loadStyleFont(String fontFileName,int style, float fontSize) {
            try{
                File file = new File(fontFileName);
                FileInputStream in = new FileInputStream(file);
                Font dynamicFont = Font.createFont(Font.TRUETYPE_FONT, in);
                Font dynamicFontPt =  dynamicFont.deriveFont(style,fontSize);
                in.close();
                return dynamicFontPt;
            }catch(Exception e) {//异常处理
    e.printStackTrace();
                return new java.awt.Font("宋体", Font.PLAIN, 40);
            }
        }
    }


    加密方法参考:https://blog.csdn.net/hht15927087748/article/details/85113002

    1、给execl加密(打开时需要密码才能看)
    2、还是给sheet加密等(可以打开查看,但是无法编辑)

    1、HSSFWorkbook wb = new HSSFWorkbook(new FileInputStream(bp.resourcesDir + "11221.xls"));

    或者HSSFWorkbook wb = new HSSFWorkbook();

    wb.writeProtectWorkbook("password", "username");

    2 、Sheet sheet = wb.createSheet("sheetName");

    sheet.protectSheet("password");

  • 相关阅读:
    str_pad 和 filter_var
    phpstorm主题下载地址
    php二维数组的排序
    wx.request出现400 bad request的问题
    php里的闭包函数
    关于宝塔下的项目中的php不能访问的问题
    字体大小适配宽度
    递归复制&查看文件夹下的指定后缀的文件
    find_in_set
    给动态ajax添加的元素添加click事件
  • 原文地址:https://www.cnblogs.com/binzhou75/p/13884377.html
Copyright © 2011-2022 走看看