技术栈
-
Spring Boot:轻量级的Java开发框架,简化开发流程。
-
EasyExcel:强大的Excel处理库,支持复杂的Excel操作。
-
Apache POI:底层库,用于处理Excel文件的读写。
-
ImageIO:Java标准库中的一个API,用于读取和写入多种常见图像格式(如JPEG、PNG、BMP、GIF等),简化了图像处理操作。
-
着重介绍一下ImageIO
-
ImageIO 是 Java 标准库提供的一个类,它提供了读取和写入多种常见图像格式的功能。开发者可以使用 ImageIO 类快速、便捷地读取和写入图像,而无需关心底层的细节。
-
ImageIO 还支持灵活的、可配置的图片格式转换。例如,可以将一张 JPEG 格式的图像读取到内存中,然后将其保存为 PNG 格式的图像。此外,ImageIO 还支持高级的图像处理操作,如裁剪、旋转、缩放、反转、镜像等。
-
ImageIO 的读取操作
- 通过 ImageIO 类的静态方法 read() 或者 write() 方法获取图片输入输出流;
-
ImageIO 的 read() 方法
ImageIO 的 read() 方法是最常见的读取图像的方式,它有多个重载形式,可以用来读取 File、InputStream、URL 和 ImageInputStream 等多种类型的输入流。此外,ImageIO 还支持从 byte 数组读取或者从任意 SeekableStream 中读取文件。
-
方法返回了一个 BufferedImage 对象,我们可以对这个对象进行操作,比如缩放、调整比例、裁剪等。这里主要使用read方法将图片读取到byte数组中
-
ImageIO 的写入操作
ImageIO 的 write() 方法
ImageIO 的 write() 方法的作用是写入图像,它可以将 BufferedImage 对象写入到文件或者输出流中。除此之外,ImageIO 还提供了其他的方法来指定写出的格式和压缩质量等参数。
下面是一个将 BufferedImage 写入 PNG 格式的简单示例:
关于imageio的具体使用可以参考这位博主的帖子,我也是参考的她的,写的很详细
核心思路
-
图片处理:使用ImageIO通过正则表达式从HTML内容中或图片的URL字符串提取图片URL,并将其转换为字节数组。
-
单元格样式:自定义单元格样式处理器,动态调整单元格大小以适应图片。
-
图片插入:利用Apache POI的绘图功能,将图片插入到指定单元格..、
实现
导入maven坐标
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.24</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>easyexcel</artifactId>
<version>3.1.2</version>
</dependency>
编写domain类
package com.lin.exceldemo.Export;
import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
import com.alibaba.excel.annotation.ExcelProperty;
import com.alibaba.excel.annotation.write.style.ColumnWidth;
import lombok.Data;
@ExcelIgnoreUnannotated//导出只带有ExcelProperty注解的字段
@Data
public class exportDataVo {
@ExcelProperty(value = "姓名")
private String name;
@ExcelProperty(value = "年龄")
private String age;
private String imageUrl;
@ExcelProperty(value = "图片")
// @ColumnWidth(40) // 增加列宽以便更好地显示图片
private byte[] image; // 将类型改为byte[]用于存储图片数据
}
编写ExcelImageExportUtil 工具类用于excel导入
完整代码
package com.lin.exceldemo.utill;
import com.alibaba.excel.metadata.Head;
import com.alibaba.excel.write.handler.CellWriteHandler;
import com.alibaba.excel.write.metadata.holder.WriteSheetHolder;
import com.alibaba.excel.write.metadata.holder.WriteTableHolder;
import com.alibaba.excel.write.metadata.style.WriteCellStyle;
import com.alibaba.excel.write.style.HorizontalCellStyleStrategy;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.util.Units;
import org.apache.poi.xssf.usermodel.XSSFClientAnchor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.net.URL;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class ExcelImageExportUtil {
private static final Logger log = LoggerFactory.getLogger(ExcelImageExportUtil.class);
private static final Pattern IMG_PATTERN = Pattern.compile("<img[^>]+src=\"([^\"]+)\"[^>]*>");
private static final Pattern TEXT_PATTERN = Pattern.compile("<p>([^<]+)</p>");
// 默认Excel单元格尺寸
private static int maxWidth = 100;
private static int maxHeight = 100;
/**
* 设置Excel单元格宽度(像素)
*/
public static void setExcelCellWidth(int width) {
maxWidth = width;
}
/**
* 设置Excel单元格高度(像素)
*/
public static void setExcelCellHeight(int height) {
maxHeight = height;
}
/**
* 获取自定义的单元格样式处理器,处理多个图片列
*
* @param imageColumnIndexes 图片列的索引数组
*/
public static CellWriteHandler getCustomCellWriteHandler(int... imageColumnIndexes) {
return new CellWriteHandler() {
@Override
public void afterCellCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder,
Cell cell, Head head, Integer relativeRowIndex, Boolean isHead) {
// 检查当前列是否是图片列
int currentColumn = cell.getColumnIndex();
boolean isImageColumn = false;
for (int imageIndex : imageColumnIndexes) {
if (currentColumn == imageIndex) {
isImageColumn = true;
break;
}
}
// 只处理图片列
if (isImageColumn) {
Sheet sheet = writeSheetHolder.getSheet();
// 设置图片列的宽度
sheet.setColumnWidth(currentColumn, 27 * 256);
// 只设置包含图片的单元格的行高
Row row = cell.getRow();
if (isHead) {
row.setHeightInPoints(30); // 标题行高度30磅
} else {
row.setHeightInPoints(80); // 数据行高度100磅
}
// 获取HTML内容并提取图片写入单元格
String urlContent = cell.getStringCellValue();
byte[] imageData = extractImageFromUrl(urlContent);
if (imageData != null) {
writeImageToCell(sheet, relativeRowIndex, currentColumn, imageData);
}
}
}
};
}
/**
* 从HTML内容中提取图片
*/
public static byte[] extractImageFromHtml(String htmlContent) {
if (htmlContent == null || htmlContent.trim().isEmpty()) {
return null;
}
try {
Matcher imgMatcher = IMG_PATTERN.matcher(htmlContent);
if (imgMatcher.find()) {
String src = imgMatcher.group(1);
if (src != null && !src.trim().isEmpty()) {
URL url = new URL(src);
BufferedImage originalImage = ImageIO.read(url);
if (originalImage != null) {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ImageIO.write(originalImage, "PNG", baos);
return baos.toByteArray();
}
}
}
} catch (Exception e) {
System.err.println("处理图片失败: " + e.getMessage() + ", 内容: " + htmlContent);
}
return null;
}
/**
* 将图片写入单元格
*/
public static void writeImageToCell(Sheet sheet, int rowIndex, int colIndex, byte[] imageData) {
Workbook wb = sheet.getWorkbook();
try (ByteArrayOutputStream bos = new ByteArrayOutputStream()) {
bos.write(imageData);
int pictureIdx = wb.addPicture(bos.toByteArray(), Workbook.PICTURE_TYPE_PNG);
Drawing drawing = sheet.createDrawingPatriarch();
ClientAnchor anchor = new XSSFClientAnchor(0, 0, 0, 0, colIndex, rowIndex, (short) (colIndex + 1), rowIndex + 1);
Picture pict = drawing.createPicture(anchor, pictureIdx);
pict.resize();
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 通过url将图片数据读取到byte[]数组中
*/
public static byte[] extractImageFromUrl(String url) {
if (url == null || url.trim().isEmpty()) {
return null;
}
try {
URL Url = new URL(url);
BufferedImage originalImage = ImageIO.read(Url);
if (originalImage != null) {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ImageIO.write(originalImage, "PNG", baos);
return baos.toByteArray();
}
} catch (Exception e) {
System.err.println("处理图片失败: " + e.getMessage() + ", 内容: " + url);
}
return null;
}
/**
* 从HTML内容中提取文本
*/
public static String extractTextFromHtml(String htmlContent) {
if (htmlContent == null || htmlContent.trim().isEmpty()) {
return null;
}
try {
Matcher textMatcher = TEXT_PATTERN.matcher(htmlContent);
if (textMatcher.find()) {
return textMatcher.group(1).trim();
}
} catch (Exception e) {
log.error("提取文本失败: {}, 内容: {}", e.getMessage(), htmlContent);
}
return null;
}
/**
* 获取Excel导出配置
*
* @param imageColumnIndexes 图片列的索引数组
*/
public static HorizontalCellStyleStrategy getExcelConfig(int[] imageColumnIndexes) {
// 标题样式
WriteCellStyle headStyle = new WriteCellStyle();
headStyle.setHorizontalAlignment(HorizontalAlignment.CENTER);
headStyle.setVerticalAlignment(VerticalAlignment.CENTER);
// 内容样式
WriteCellStyle contentStyle = new WriteCellStyle();
contentStyle.setHorizontalAlignment(HorizontalAlignment.CENTER);
contentStyle.setVerticalAlignment(VerticalAlignment.CENTER);
return new HorizontalCellStyleStrategy(headStyle, contentStyle);
}
}
最后一步编写测试类测试实现效果
package com.lin.exceldemo.Export;
import com.alibaba.excel.EasyExcel;
import com.lin.exceldemo.utill.ExcelImageExportUtil;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.io.IOException;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.List;
@RestController
public class exportImageTest {
@RequestMapping("/exportImage")
public void exportTest(HttpServletResponse response) throws IOException {
// 创建一个 List 集合
List<exportDataVo> dataList = new ArrayList<>();
// 创建第一个 exportDataVo 对象
exportDataVo data1 = new exportDataVo();
data1.setName("刘诗诗");
data1.setAge("18");
data1.setImageUrl("https://tse1-mm.cn.bing.net/th/id/OIP-C.-KE-vZxx5d5fxV35EiRAGAHaGT?rs=1&pid=ImgDetMain"); //
data1.setImage(ExcelImageExportUtil.extractImageFromUrl(data1.getImageUrl()));
// 创建第二个 exportDataVo 对象
exportDataVo data2 = new exportDataVo();
data2.setName("胡歌");
data2.setAge("30");
data2.setImageUrl("https://ts1.cn.mm.bing.net/th/id/R-C.6dbdb0cc12426a4ed89a80d940c7181a?rik=eQMadfKYAXwXaQ&riu=https%3a%2f%2fblue-sea-697d.quartiers047.workers.dev%3a443%2fhttp%2fimg.tvzn.com%2fphoto%2f38458.jpg&ehk=puXo9LvWjDc1ncDAXrZaccYlKi1EHe5q1qSOxQP%2bmf4%3d&risl=&pid=ImgRaw&r=0");
data2.setImage(ExcelImageExportUtil.extractImageFromUrl(data2.getImageUrl()));//
// 添加到 List 集合中
dataList.add(data1);
dataList.add(data2);
ExcelImageExportUtil.setExcelCellWidth(100); // 设置列宽为1000像素
ExcelImageExportUtil.setExcelCellHeight(100); // 设置行高为150像素
response.setContentType("application/vnd.ms-excel");
response.setCharacterEncoding("utf-8");
String fileName = URLEncoder.encode("仙剑三", "UTF-8").replaceAll("\\+", "%20");
response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName + ".xlsx");
System.out.println("ExportImage endpoint is triggered!");
// 导出Excel
EasyExcel.write(response.getOutputStream(), exportDataVo.class)
.registerWriteHandler(ExcelImageExportUtil.getExcelConfig(new int[]{2})) // 设置基本样式
.registerWriteHandler(ExcelImageExportUtil.getCustomCellWriteHandler(2)) // 设置图片列样式
.sheet("仙剑三")
.doWrite(dataList);
}
}
测试实现效果
访问接口地址,测试效果,自行调整