Java生成报表数据图片

时间:2022-07-23
本文章向大家介绍Java生成报表数据图片,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

最近在业务需求开发当中,有一个需求是按照报告的格式生成数据图片推送到企业微信群消息当中,企业微信消息倒是还好,这生成图片的还是第一次遇到啊,然后百度了一下发现也没有什么现成的框架可以用,只能自动手写一个工具类,我先在这里放出来,怕自己以后也要用哈哈哈。废话不多说,直接上代码了!

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.ReflectUtil;
import cn.hutool.core.util.StrUtil;
import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.*;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.*;
import java.util.List;
/**
 * @author zhanbo
 * @version 1.0
 * @describe 发送图片工具类
 * @date 2020/8/13-11:06
 */
public class ImgUtils {
    private ImgUtils() {

    }

    /**
     * 十六进制值
     */
    private static char[] HEX_Digits = { '0', '1', '2', '3', '4', '5', '6',  '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };

    /**
     * 默认图片字体格式
     */
    private static final String DEFAULT_IMG_FONT_FORMAT = "微软雅黑";

    /**
     * 默认图片格式
     */
    private static final String DEFAULT_IMG_FORMAT = "png";

    /**
     * 加密方式
     */
    private static final String MD5 = "MD5";

    /**
     * 默认字体大小
     */
    private static final int DEFAULT_FONT_SIZE = 15;

    /**
     * 默认列宽
     */
    private static final int DEFAULT_CLO_SIZE = 300;

    /**
     * 校验参数
     * @param titleList 标题
     * @param dataList 数据
     */
    private static void check(List<Title> titleList, List<?> dataList) {
        /*|| CollectionUtil.isEmpty(dataList) 产品要求,数据为空发送空图片*/
        if (CollectionUtil.isEmpty(titleList) ) {
            throw new RuntimeException("titleList is null or dataList is null");
        }
    }

    /**
     * 列表数据转图片Base64编码 和 图片Md5加密字符串
     * @param totalTitle 总标题,允许为空
     * @param titleList 标题
     * @param dataList 数据
     * @return r1: 图片Base64编码,r2: 图片Md5加密字符串
     */
   /* public static DoubleResult<String, String> dataToImgBase64AndMd5(String totalTitle, List<Title> titleList, List<?> dataList) {
        check(titleList, dataList);

        byte[] bytes = dataToByteArray(totalTitle, titleList, dataList);
        return DoubleResult.buildDoubleResult(bytesToBase64(bytes), bytesToMd5(bytes));
    }*/

    /**
     * 列表数据转图片Base64编码
     * @param totalTitle 总标题,允许为空
     * @param titleList 标题
     * @param dataList 数据
     * @return
     */
    public static String dataToBase64(String totalTitle, List<Title> titleList, List<?> dataList) {
        check(titleList, dataList);

        byte[] bytes = dataToByteArray(totalTitle, titleList, dataList);
        return bytesToBase64(bytes);
    }

    /**
     * 列表数据转图片Md5
     * @param totalTitle 总标题,允许为空
     * @param titleList 标题
     * @param dataList 数据
     * @return
     */
    public static String dataToMd5(String totalTitle, List<Title> titleList, List<?> dataList) {
        check(titleList, dataList);

        byte[] bytes = dataToByteArray(totalTitle, titleList, dataList);
        return bytesToMd5(bytes);
    }

    /**
     * 列表数据转图片字节数组
     * @param totalTitle 总标题,允许为空
     * @param titleList 标题
     * @param dataList 数据
     * @return
     */
    private static byte[] dataToByteArray(String totalTitle, List<Title> titleList, List<?> dataList) {
        String[][] cellsValue = dataToCellsValue(titleList, dataList);
        BufferedImage bufferedImage = arrayToImage(totalTitle, cellsValue);
        byte[] bytes = imageToBytes(bufferedImage);
        return bytes;
    }

    /**
     * 集合数据转为数组,用于将转换后的二维数组转为图片
     * @param titleList 标题
     * @param dataList 数据
     * @return
     */
    private static String[][] dataToCellsValue(List<Title> titleList, List<?> dataList) {
        String[][] cellsValue = new String[dataList.size()+1][titleList.size()];
        /*初始化标题列*/
        for (int index = 0 ; index< titleList.size();index++){
            cellsValue[0][index] = titleList.get(index).getName();
        }
        /*填充数据*/
        for (int i = 0; i < dataList.size(); i++) {
            Object rowData = dataList.get(i);
            for (int j = 0; j < titleList.size(); j++) {
                Object fieldValue = ReflectUtil.getFieldValue(rowData, titleList.get(j).fieldName);
                if (fieldValue instanceof Date) {
                    cellsValue[i+1][j] = DateUtil.format((Date) fieldValue, "yyyy-MM-dd");
                }else {
                    cellsValue[i+1][j] = fieldValue == null ? "" : fieldValue.toString();
                }
            }
        }
        return cellsValue;
    }

    /**
     * 数组数据转为图片
     * @param totalTitle 总标题,允许为空
     * @param cellsValue 数组数据
     *
     * @return
     */
    private static BufferedImage arrayToImage(String totalTitle, String[][] cellsValue) {
        // 字体大小
        int fontTitleSize = DEFAULT_FONT_SIZE;
        // 横线的行数
        int totalRow = cellsValue.length + 1;
        // 竖线的行数
        int totalCol = 0;
        if (cellsValue[0] != null) {
            totalCol = cellsValue[0].length;
        }
        // 图片宽度,每个列宽300
        int imageWidth = totalCol*DEFAULT_CLO_SIZE;
        // 行高
        int rowHeight = 40;
        // 图片高度
        int imageHeight = totalRow * rowHeight + 50;
        // 起始高度
        int startHeight = 10;
        // 起始宽度
        int startWidth = 15;
        // 单元格宽度
        int colWidth = (imageWidth - 20) / totalCol;

        BufferedImage image = new BufferedImage(imageWidth, imageHeight, BufferedImage.TYPE_INT_RGB);
        Graphics graphics = image.getGraphics();
        graphics.setColor(Color.WHITE);
        graphics.fillRect(0, 0, imageWidth, imageHeight);
        graphics.setColor(new Color(220, 240, 240));

        // 画横线
        for (int j = 0; j < totalRow; j++) {
            graphics.setColor(Color.black);
            graphics.drawLine(startWidth, startHeight + (j + 1) * rowHeight, startWidth + colWidth * totalCol, startHeight + (j + 1) * rowHeight);
        }
        // 画竖线
        for (int k = 0; k < totalCol + 1; k++) {
            graphics.setColor(Color.black);
            graphics.drawLine(startWidth + k * colWidth, startHeight + rowHeight, startWidth + k * colWidth, startHeight + rowHeight * totalRow);
        }
        // 设置字体
        Font font = new Font(DEFAULT_IMG_FONT_FORMAT, Font.BOLD, fontTitleSize);
        graphics.setFont(font);
        // 写入总标题
        if (StrUtil.isNotEmpty(totalTitle)) {
            /*图片标题位置*/
            graphics.drawString(totalTitle, imageWidth/2, startHeight + rowHeight - 10);
        }
        // 写入内容
        for (int n = 0; n < cellsValue.length; n++) {
            for (int l = 0; l < cellsValue[n].length; l++) {
                // 第一行为列名
                if (n == 0) {
                    font = new Font(DEFAULT_IMG_FONT_FORMAT, Font.BOLD, fontTitleSize);
                    graphics.setFont(font);
                } else {
                    // 其余行为内容
                    font = new Font(DEFAULT_IMG_FONT_FORMAT, Font.PLAIN, fontTitleSize);
                    graphics.setFont(font);
                    graphics.setColor(Color.BLACK);
                }
                graphics.drawString(cellsValue[n][l], startWidth + colWidth * l + 5, startHeight + rowHeight * (n + 2) - 10);
            }
        }
        return image;
    }

    /**
     * 图片流转字节数组
     * @param image 图片流
     * @return
     */
    private static byte[] imageToBytes(BufferedImage image) {
        ByteArrayOutputStream os = new ByteArrayOutputStream();
        InputStream is = null;
        try {
            ImageIO.write(image, DEFAULT_IMG_FORMAT, os);
            is = new ByteArrayInputStream(os.toByteArray());
            byte[] bytes = new byte[is.available()];
            is.read(bytes);
            return bytes;
        } catch (IOException e) {
            throw new RuntimeException(e);
        } finally {
            try {
                if (is != null) {
                    is.close();
                }
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }
    }

    /**
     * 字节数组进行Base64编码
     * @param bytes 字节数组
     * @return
     */
    private static String bytesToBase64(byte[] bytes) {
        Base64.Encoder encoder = Base64.getEncoder();
        return encoder.encodeToString(bytes);
    }

    /**
     * 字节数组进行MD5加密
     * @param bytes 字节数组
     * @return
     */
    private static String bytesToMd5(byte[] bytes) {
        MessageDigest messagedigest = null;
        try {
            messagedigest = MessageDigest.getInstance(MD5);
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException(e);
        }
        messagedigest.update(bytes);
        return bufferToHex(messagedigest.digest());
    }

    /**
     * 字节数组转16进制
     * @param bytes 字节数组
     * @return
     */
    private static String bufferToHex(byte [] bytes) {
        return bufferToHex(bytes, 0, bytes.length);
    }

    /**
     * 字节数组转16进制
     * @param bytes 字节数组
     * @param m 起始位
     * @param n 结束位
     * @return
     */
    private static String bufferToHex(byte[]  bytes, int m, int n) {
        StringBuffer stringbuffer = new StringBuffer(2 * n);
        int k = m + n;
        for (int l = m; l < k; l++) {
            char c0 = HEX_Digits[(bytes[l] & 0xf0) >> 4];
            char c1 = HEX_Digits[bytes[l] & 0xf];
            stringbuffer.append(c0);
            stringbuffer.append(c1);
        }
        return stringbuffer.toString();
    }


    /**
     * 将字节数组转换为文件图片
     * @param filename
     * @param data
     * @throws Exception
     */
    public static void saveFile(String filename,byte [] data)throws Exception{
        if(data != null){
            String filepath ="D:\" + filename+".png";
            File file  = new File(filepath);
            if(file.exists()){
                file.delete();
            }
            FileOutputStream fos = new FileOutputStream(file);
            fos.write(data,0,data.length);
            fos.flush();
            fos.close();
        }
    }

    /**
     * 标题
     */
    public static class Title {

        /**
         * 字段名称
         */
        private String fieldName;

        /**
         * 标题名称
         */
        private String name;

        public Title(String fieldName, String name) {
            this.fieldName = fieldName;
            this.name = name;
        }

        public String getFieldName() {
            return fieldName;
        }

        public String getName() {
            return name;
        }
    }
}

工具的使用比较简单,直接生成标题,往里面丢数据就行了,因为企业微信发送图片的要求是图片的md5格式和base64格式,所以方法也都提供出来了。