Java将数据集合转换导出为图片

news2024/11/24 4:24:34

将数据集合导出为图片

Java将数据集合转换导出为根据数据自适应大小的图片,并且保证数据的完整展示

工具类代码

package xxxxxxxxx;

import cn.hutool.core.date.DateTime;

import javax.imageio.ImageIO;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletResponse;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.sql.Timestamp;
import java.text.SimpleDateFormat;
import java.util.*;
import java.awt.*;
import java.awt.font.FontRenderContext;
import java.awt.geom.Rectangle2D;
import java.util.List;

/**
 * TO Image Utils
 *
 * @author 林夕风暴
 * @Date 2023/7/19
 **/
public class ToImageUtils {

    /*** 字体设置 */
    private static Font font = new Font(null, Font.PLAIN, 12);

    /** 固定的边缘空白区域大小 */
    private static Integer margin = 20;

    /** 列间隔像素 */
    private static Integer columnInterval = 60;

    /** 行间隔像素 */
    private static Integer rowInterval = 20;

    /**
     * 生成图片 通过浏览器下载
     * 图片格式 png
     *
     * @param dataList
     * @param response
     * @param fileName
     * @throws ServletException
     * @throws IOException
     */
    public static void dataToImageForBrowserDownload(List<Map<String, Object>> dataList,
                                                     List<String> columnNameList,
                                                     HttpServletResponse response,
                                                     String fileName
                                                    )  throws ServletException, IOException{
        // 转换实体数据集合为自适应大小的图片
        BufferedImage image = convertToImage(dataList,columnNameList);
        
        String currentTimeStr = cn.hutool.core.date.DateUtil.format(new DateTime(new Date().getTime()), "yyyyMMddHHmmss");
        fileName = fileName + currentTimeStr + ".png";
        response.reset();
        response.setCharacterEncoding("utf-8");
        fileName = java.net.URLEncoder.encode(fileName,"UTF-8").replace("+", "%20");
        // 设置响应头,将图片以PNG格式进行下载
        response.setHeader("Content-Disposition", "attachment; filename*=UTF-8''" + fileName);
        response.setContentType("image/png");

        // 将图片写入响应输出流
        ImageIO.write(image, "png", response.getOutputStream());

    }

    public static BufferedImage convertToImage(List<Map<String, Object>> sourceList,
                                                List<String> columnNameList) throws IOException {

        //List<Map<String, Object>> to List<Map<String, String>>
        List<Map<String, String>> dataList = convertToStringList(sourceList);
        //找到List<Map<String, String>>其中的最大字符串及其长度
        Map.Entry<String, Integer> maxStringNew = findMaxStringNew(dataList);
        //根据最长的字符串判断需要的单元格宽度和高度
        int[] cellWidthAndHeight = calculateCellWidthAndHeight(maxStringNew.getKey());
        int cellWidth = cellWidthAndHeight[0];
        int cellHeight = cellWidthAndHeight[1];
        //计算要生成的图片大小
        int[] imageSize = calculateImageSize(dataList, cellWidth, cellHeight);
        // 图像宽度、图像高度
        int width = imageSize[0];
        int height = imageSize[1];
        BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
        Graphics2D graphics = image.createGraphics();
        graphics.setBackground(Color.WHITE);
        graphics.clearRect(0, 0, width, height);

        graphics.setFont(font);
        graphics.setColor(Color.BLACK);

        // 绘制表格线
        int x1 = margin - 10;
        int x2 = width - margin;
        int y1 = margin - cellHeight;
        int y2 = height - margin;
        // 顶部横线、底部横线
        graphics.drawLine(x1, y1, x2, y1);
        graphics.drawLine(x1, y2, x2, y2);
        graphics.drawLine(x1, y1, x1, y2);
        graphics.drawLine(x2, y1, x2, y2);

        // 内部横线、内部竖线
        for (int i = 1; i < dataList.size(); i++) {
            int y = (margin - cellHeight) + i * (cellHeight + rowInterval);
            graphics.drawLine(x1, y, x2, y);
        }

        for (int i = 1; i < dataList.get(0).keySet().size(); i++) {
            int x = margin + i * (cellWidth + columnInterval) - 10;
            graphics.drawLine(x, y1, x, y2);
        }

        // 文本绘制的初始X坐标、文本绘制的初始Y坐标
        int x = margin;
        int y = margin;

        for (Map<String, String> row : dataList) {
//            for (String value : row.values()) {
//                String cellValue = value;
//                graphics.drawString(cellValue, x, y);
//                // 每列文本间隔60像素
//                x += columnInterval + cellWidth;
//            }
            for (String columnName : columnNameList) {
                String cellValue = row.get(columnName);
                graphics.drawString(cellValue, x, y);
                // 每列文本间隔60像素
                x += columnInterval + cellWidth;
            }

            // 重置X坐标为初始值,每行文本间隔20像素
            x = margin;
            y += rowInterval + cellHeight;
        }

        graphics.dispose();

        return image;
    }

    /**
     * 计算要生成的图片的大小
     *
     * @param data
     * @param cellWidth  列宽
     * @param cellHeight 行高
     * @return
     */
    public static int[] calculateImageSize(List<Map<String, String>> data, int cellWidth, int cellHeight) {
        //行数、列数
        int numRows = data.size();
        int numCols = data.get(0).keySet().size();

        // 计算图像宽度
        int imageWidth = numCols * cellWidth + 2 * margin + (numCols - 1) * columnInterval;

        // 计算图像高度
        int imageHeight = numRows * cellHeight + 2 * margin + (numRows - 1) * rowInterval;

        return new int[]{imageWidth, imageHeight};
    }

    /**
     * 根据字符串的长度判断需要的单元格宽度和高度
     * 使用字体和字号来确定字符串的像素宽度和像素高度
     *
     * @param text
     * @return
     */
    public static int[] calculateCellWidthAndHeight(String text) {
        FontRenderContext frc = new FontRenderContext(null, true, true);
        Rectangle2D bounds = font.getStringBounds(text, frc);
        int textWidth = (int) bounds.getWidth();
        int textHeight = (int) bounds.getHeight();

        // 添加一些额外的空白边距
        int padding = 10;

        return new int[]{textWidth + padding, textHeight};
    }

    /**
     * 找到List<Map<String, Object>>其中的最大字符串及其长度
     *
     * @param sourceList
     * @return
     */
    public static Map.Entry<String, Integer> findMaxString(List<Map<String, Object>> sourceList) {
        List<Map<String, String>> list = convertToStringList(sourceList);
        String maxString = null;
        int maxLength = 0;

        for (Map<String, String> map : list) {
            for (String stringValue : map.values()) {
                int length = stringValue.length();
                if (length > maxLength) {
                    maxLength = length;
                    maxString = stringValue;
                }
            }
        }

        return new AbstractMap.SimpleEntry<>(maxString, maxLength);
    }

    /**
     * 找到List<Map<String, String>>其中的最大字符串及其长度
     *
     * @param list
     * @return
     */
    public static Map.Entry<String, Integer> findMaxStringNew(List<Map<String, String>> list) {
        String maxString = null;
        int maxLength = 0;

        for (Map<String, String> map : list) {
            for (String stringValue : map.values()) {
                int length = stringValue.length();
                if (length > maxLength) {
                    maxLength = length;
                    maxString = stringValue;
                }
            }
        }

        return new AbstractMap.SimpleEntry<>(maxString, maxLength);
    }

    /**
     * List<Map<String, Object>> TO List<Map<String, String>>
     *
     * @param originalList
     * @return
     */
    public static List<Map<String, String>> convertToStringList(List<Map<String, Object>> originalList) {
        List<Map<String, String>> resultList = new ArrayList<>();

        for (Map<String, Object> originalMap : originalList) {
            Map<String, String> resultMap = convertToStringMap(originalMap);
            resultList.add(resultMap);
        }

        return resultList;
    }

    public static Map<String, String> convertToStringMap(Map<String, Object> originalMap) {
        Map<String, String> resultMap = new HashMap<>();

        for (Map.Entry<String, Object> entry : originalMap.entrySet()) {
            String key = entry.getKey();
            Object value = entry.getValue();

            // 根据值的类型进行处理
            String stringValue;
            if (value instanceof Map) {
                // 如果值是Map类型,则递归处理
                stringValue = convertToStringMap((Map<String, Object>) value).toString();
            } else {
                // 其他类型的值转换为String类型
                stringValue = convertValueToString(value);
            }

            resultMap.put(key, stringValue);
        }

        return resultMap;
    }

    private static String convertValueToString(Object value) {
        if (value == null) {
            return "";
        } else if (value instanceof Date) {
            // 对Date类型进行特殊处理
            SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            return dateFormat.format((Date) value);
        } else if (value instanceof Boolean) {
            // 如果值是布尔类型,则转换为字符串表示
            return Boolean.toString((Boolean) value);
        } else {
            // 默认情况下使用toString()方法转换为String类型
            return value.toString();
        }
    }

}

测试代码

    @GetMapping("/testImage")
    public void testImage(HttpServletResponse httpServletResponse) throws ServletException, IOException {
        List<Map<String, Object>> dataList = new ArrayList<>();
        dataList.add(new HashMap<String, Object>() {{
            put("name", "姓名");
            put("age", "年龄");
        }});
        for (int i = 0; i < 100; i++) {
            Map<String, Object> itemMap = new HashMap<>();
            itemMap.put("name", "张" + i);
            itemMap.put("age", new Random().nextInt(50) + 1);
            dataList.add(itemMap);
        }
        String fileName = "测试";
        ToImageUtils.dataToImageForBrowserDownload(dataList, Arrays.asList("name", "age"), httpServletResponse, fileName);
    }

效果

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/777453.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

linux部署前后端项目

使用nginx部署前端vue项目&#xff1a; nginx的安装就不在过了&#xff0c;直接打包vue项目&#xff1a; npm run build运行完之后会生成一个dist文件夹 将dist文件夹复制到linux文件中&#xff0c;我这里放到了/usr/local/vue目录下&#xff1a; 打开nginx的conf目录下的ngi…

Vue3通透教程【十六】TS编译配置

文章目录 &#x1f31f; 写在前面&#x1f31f; 初始化配置文件⭐ target⭐ module⭐ lib⭐ types/node⭐ include⭐ outDir&#x1f31f; 写在最后 &#x1f31f; 写在前面 专栏介绍&#xff1a; 凉哥作为 Vue 的忠实 粉丝输出过大量的 Vue 文章&#xff0c;应粉丝要求开始更…

muduo库的log

muduo 库里的日志打印比较复杂&#xff0c;跟标准库 std::cout << 似乎比较像吧&#xff0c;库里自己实现了“流”式的日志打印&#xff0c;今天一起来看一下。 int main(int argc, char* argv[]) {CLogger::setLogLevel(CLogger::INFO);LOG_INFO << "loggin…

Xcode 15 beta 4 (15A5195m) - Apple 平台 IDE

Xcode 15 beta 4 (15A5195m) - Apple 平台 IDE IDE for iOS/iPadOS/macOS/watchOS/tvOS/visonOS 请访问原文链接&#xff1a;https://sysin.org/blog/apple-xcode-15/&#xff0c;查看最新版。原创作品&#xff0c;转载请保留出处。 作者主页&#xff1a;sysin.org visonOS …

07 QT信号与槽重载问题及解决

Tips&#xff1a; 参数重载时需要函数指针明确重载的是哪一个&#xff0c;避免出现二义性 案例修改&#xff1a; 重载学生类中的treat函数&#xff0c;增加foodName参数 //Teacher.h #ifndef TEACHER_H #define TEACHER_H#include <QObject>class Teacher : public QObje…

Jmeter安装及快速入门(超详细教程)

目录 1.安装Jmeter 1.1.下载 1.2.解压 1.3.运行 2.快速入门 2.1.设置中文语言 2.2.基本用法 1.安装Jmeter Jmeter依赖于JDK&#xff0c;所以必须确保当前计算机上已经安装了JDK&#xff0c;并且配置了环境变量。 1.1.下载 可以Apache Jmeter官网下载&#xff0c;地址&am…

18.matlab数据分析多项式的拟合(matlab程序)

1.简述 解决数据拟合问题最重要方法是最小二乘法和回归分析。如&#xff0c;我们需要从一组测定的数据&#xff08;例如N个点&#xff08;xi&#xff0c;yi&#xff09;&#xff08;i0,1&#xff0c;…&#xff0c;m&#xff09;&#xff09;去求得自变量 x 和因变量 y 的一个近…

解决Vue2中控制台报错 [WDS] Disconnected! 问题

Vue2 项目打开控制台时发现如下报错 &#xff1a; [WDS] Disconnected! client?9556:172 其实并没有对项目运行本身造成什么实质性的影响&#xff0c;但是一条红色的提示摆在那里确实不太好看&#xff0c;还是把他给解决掉吧。 在网上看好多人说需要将 config…

django实现好看的翻页分页效果,封装翻页组件,实现在任意页面实现翻页功能。

一、实现目标 在做web开发的时候,我们经常需要显示表格或者列表类型的数据,当数据比较多的时候,我们不会一次性全部显示出来,而是一次只显示固定数量的数据,当要查看其他数据时,点击翻页进行查看,而这个功能就是我们实现的分页、翻页功能。 话不多说,我们期望实现的翻页…

opencv-15 数字水印原理

最低有效位&#xff08;Least Significant Bit&#xff0c;LSB&#xff09;指的是一个二进制数中的第 0 位&#xff08;即最低位&#xff09;。 最低有效位信息隐藏指的是&#xff0c;将一个需要隐藏的二值图像信息嵌入载体图像的最低有效位&#xff0c;即将载体图像的最低有效…

央视报道!北京筑龙深度参编的 《企业采购供应链数字化成熟度模型》开始实施

7月12日&#xff0c;由中国物流与采购联合会牵头&#xff0c;北京筑龙深度参与编写的《企业采购供应链数字化成熟度模型》团体标准在“第四届国有企业数智化采购与智慧供应链论坛”上重磅发布。该标准于7月15日起正式实施&#xff0c;并将在企业中开展模型应用评估试点工作。 图…

服务器量化训练操作说明

Freespace服务器预训练主要步骤&#xff1a; 首先登录堡垒机&#xff0c;命令如下&#xff1a; ssh xxxrelay.baidu-int.com &#xff08;xxx为个人邮箱前缀&#xff09; 密码为个人邮箱密码 登录工作机&#xff0c;命令如下&#xff1a; ssh l3yq01-gpu-255-122-22-00.e…

Redis源码篇 - inset数据结构

inset是Redis中set类型的一种底层存储结构&#xff08;编码&#xff09;&#xff0c;它是基于整数数组来实现的&#xff0c;用于存储数值类型set集合数据&#xff0c;并具备长度可变、有序等特征。 有序性 为了方便查找&#xff0c;Redis会将intset中整数数据按照从小到大的顺…

017-从零搭建微服务-系统服务(四)

写在最前 如果这个项目让你有所收获&#xff0c;记得 Star 关注哦&#xff0c;这对我是非常不错的鼓励与支持。 源码地址&#xff08;后端&#xff09;&#xff1a;https://gitee.com/csps/mingyue 源码地址&#xff08;前端&#xff09;&#xff1a;https://gitee.com/csps…

【服务器】零成本搭建网站并内网穿透实现公网访问

零成本搭建网站并内网穿透实现公网访问 在普通电脑用户看来&#xff0c;建立自己的网站总是一件高大上的事情&#xff0c;这个网站不仅可以成为展示自己的平台&#xff0c;还能成为商业的载体。在以往&#xff0c;建立一个像样的网站&#xff0c;不仅需要过硬的编程知识做基础…

在外远程桌面控制家里的电脑

在外远程桌面控制家里的电脑 电脑作为现代家庭不可或缺的电子设备之一&#xff0c;早已在广大家庭中普及。不仅年轻人需要在工作、生活中使用电脑&#xff0c;在老家的父母也在熟悉和使用电脑。但身在外乡打工的我们&#xff0c;总会碰到家中父母抱怨电脑卡、慢、不好用的情况…

微信小程序学习笔记(四)——自定义组件

自定义组件 组件的创建与引用 创建组件 在根目录下创建 components 文件夹右键点击 components 文件夹&#xff0c;选择新建 Component&#xff0c;就会自动生成.wxml、.wxss、.js、.json文件 引用组件 组件的引用方式分为“局部引用”和“全局引用”&#xff0c;故名思义…

【指针的进阶(3)】回调函数和qsort排序各种类型的数据

文章目录 前言一、回调函数是什么&#xff1f;如何实现回调函数 二、回调函数的应用——qsortqsort排序各种类型的数据 总结 前言 前两章讲了指针的类型&#xff0c;数组传参和指针传参&#xff0c;还有函数指针和函数指针数组&#xff0c;接下来第三章讲回调函数 指针函数非常…

MySQL-运维

1、日志 1.1 错误日志 错误日志是MysQL中最重要的日志之一&#xff0c;它记录了当mysqld启动和停止时&#xff0c;以及服务器在运行过程中发生任何严重错误时的相关信息。当数据库出现任何故障导致无法正常使用时&#xff0c;建议首先查看此日志。 该日志是默认开启的&#…

高阶C语言|结构体,枚举,联合--自定义类型的使用计算

自定义类型--结构体&#xff0c;枚举&#xff0c;联合 一、结构体1.1结构体类型的声明1.1.1结构的基础知识1.1.2结构的声明1.1.3特殊的声明 1.2结构体的引用1.3结构体变量的定义和初始化1.4结构体内存对齐1.4.1修改默认对齐数 1.5结构体传参1.6结构体实现位段&#xff08;位段的…