将数据集合导出为图片
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);
}