SpringBoot + EasyExcel + Apache POI 实现excel文件导入,文件中包含图片
SpringBoot + EasyExcel + Apache POI 实现excel文件导入,文件中包含图片
EasyExcel是一个基于Java的、快速、简洁、解决大文件内存溢出的Excel处理工具。他能让你在不用考虑性能、内存的等因素的情况下,快速完成Excel的读、写等功能。EasyExcel的优势外,对于文件中包含图片的处理没有完美的解决方案,结合Apache POI 完成依然可以快速该问题的完美处理。
下面是一个使用 Spring Boot 实现 Excel 文件导入,且文件中包含图片的示例。这里使用 Apache POI 库来处理 Excel 文件,使用 MultipartFile 接收前端上传的文件。下面以.xslx的文件为例
添加依赖
在 pom.xml 中添加必要的依赖:
<dependencies>
<!-- Spring Boot Web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Apache POI for Excel -->
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
<version>4.1.2</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>4.1.2</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>easyexcel</artifactId>
<version>3.3.4</version>
<scope>compile</scope>
</dependency>
</dependencies>
需要注意的是,EasyExcel包下是有对poi的依赖,如果说想使用poi 5.x.x版本,需要排除EasyExcel下对POI的依赖,不然会有版本兼容的问题,在EasyExcel的官网上也有明确的说明。
创建和EasyExcel读文件相关的类
- 数据模型:
ExcelData类用于映射 Excel 中的数据
import com.alibaba.excel.annotation.ExcelIgnore;
import com.alibaba.excel.annotation.ExcelProperty;
import lombok.Data;
/**
* 封装数据的实体
*/
@Data
public class ExcelData {
@ExcelProperty(index = 0, value = "名称")
private String name;
// 该字段用于保存数据所在行的行号
@ExcelIgnore
private Integer rowId;
}
- 导入文件的监听器,
ExcelDataListener类继承自AnalysisEventListener,用于处理读取到的数据。invoke方法在每一行数据解析完成后被调用,doAfterAllAnalysed方法在所有数据解析完成后被调用,在这个方法里可以对图片进行保存等操作。具体的实现可以根据业务自行实现。
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import java.util.ArrayList;
import java.util.List;
public class ExcelDataListener extends AnalysisEventListener<ExcelData> {
private List<ExcelData> dataList = new ArrayList<>();
@Override
public void invoke(ExcelData data, AnalysisContext context) {
// 获取数据所在行的行号,后续和文件中的图片数据做匹配使用
Integer rowIndex = context.readRowHolder().getRowIndex();
data.setRowId(rowIndex);
dataList.add(data);
}
@Override
public void doAfterAllAnalysed(AnalysisContext context) {
}
public List<ExcelData> getDataList() {
return dataList;
}
}
- ImportExcelPuctureController,导入文件的Controller,接收上传的 Excel 文件
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.IdUtil;
import com.alibaba.excel.EasyExcel;
import org.apache.poi.ss.usermodel.Drawing;
import org.apache.poi.ss.usermodel.PictureData;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.xssf.usermodel.*;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;
/**
* @description 导入excel文件,其中文件内容包含图片
*/
@RestController
@RequestMapping("/test")
public class ImportExcelPuctureController {
@GetMapping("/import")
public void importExcelWithPucture(@RequestParam("file") MultipartFile file) {
ExcelDataListener excelDataListener = new ExcelDataListener();
try (InputStream inputStream1 = file.getInputStream()) {
// EasyExcel 读取文件中的内容
EasyExcel.read(inputStream1, ExcelData.class, excelDataListener).sheet().doRead();
// 使用 Apache POI 读取图片
InputStream inputStream = file.getInputStream();
Workbook workbook = new XSSFWorkbook(inputStream);
Sheet sheet = workbook.getSheetAt(0);
Map<Integer, Map<Integer, PictureData>> pictureMap = getPictures(sheet);
List<ExcelData> dataList = excelDataListener.getDataList();
Map<Integer, ExcelData> map = dataList.stream().collect(Collectors.toMap(ExcelData::getRowId, Function.identity()));
// 将easyexcel读到的数据和图片进行匹配,如果有云存储 阿里云、者华为云或者minio,可以将文件上传后,把上传后的文件地址和数据匹配。
for (Map.Entry<Integer, ExcelData> dataEntry : map.entrySet()) {
Integer rowId = dataEntry.getKey();
if (pictureMap.containsKey(rowId)) {
Map<Integer, PictureData> integerPictureDataMap = pictureMap.get(dataEntry.getKey());
for (Map.Entry<Integer, PictureData> pictureDataEntry : integerPictureDataMap.entrySet()) {
int colId = pictureDataEntry.getKey();
PictureData pictureData = pictureDataEntry.getValue();
byte[] pictureBytes = pictureData.getData();
File targetFile = new File("your path" + IdUtil.getSnowflake().nextIdStr() + rowId + "-" + colId + getImageSuffix(pictureData));
File file1 = FileUtil.writeBytes(pictureBytes, targetFile);
System.out.println(file1);
// 组装数据,可以保存到对应的数据源中
System.out.println("data row " + rowId + " ExcelData data " + dataEntry.getValue() + " Picture in row " + rowId + " col index " + colId + " size: " + pictureBytes.length);
}
}
}
} catch (IOException e) {
throw new RuntimeException(e);
}
}
// 根据文件头信息,得到对应的文件后缀名,文件类型提供13+种,下面罗列了一些常用的文件后缀名
private static String getImageSuffix(PictureData pictureData) {
int pictureType = pictureData.getPictureType();
return switch (pictureType) {
case Workbook.PICTURE_TYPE_JPEG -> ".jpg";
case Workbook.PICTURE_TYPE_PNG -> ".png";
case Workbook.PICTURE_TYPE_DIB -> ".dib";
case XSSFWorkbook.PICTURE_TYPE_GIF -> ".gif";
case Workbook.PICTURE_TYPE_EMF -> ".emf";
case Workbook.PICTURE_TYPE_WMF -> ".wmf";
case Workbook.PICTURE_TYPE_PICT -> ".pict";
case XSSFWorkbook.PICTURE_TYPE_WPG -> ".wpg";
case XSSFWorkbook.PICTURE_TYPE_TIFF -> ".tif";
case XSSFWorkbook.PICTURE_TYPE_EPS -> ".eps";
case XSSFWorkbook.PICTURE_TYPE_BMP -> ".bmp";
default -> "未知后缀名";
};
}
// 处理图片
// 放回结果格式说明:Map<行号,Map<列号,PictureData对象>>
private Map<Integer, Map<Integer, PictureData>> getPictures(Sheet sheet) {
Map<Integer, Map<Integer, PictureData>> pictureMap = new HashMap<>();
Drawing<?> drawing = sheet.getDrawingPatriarch();
if (drawing == null) {
drawing = sheet.createDrawingPatriarch();
}
if (drawing instanceof XSSFDrawing xssfDrawing) {
for (XSSFShape shape : xssfDrawing.getShapes()) {
if (shape instanceof XSSFPicture picture) {
XSSFClientAnchor anchor = picture.getClientAnchor();
int row = anchor.getRow1();
short col = anchor.getCol1();
System.out.println("row " + row + " col " + col);
PictureData pictureData = picture.getPictureData();
if (pictureMap.containsKey(row)) {
Map<Integer, PictureData> integerPictureDataMap = pictureMap.get(row);
integerPictureDataMap.put((int) col, pictureData);
pictureMap.put(row, integerPictureDataMap);
} else {
Map<Integer, PictureData> pictureSubMap = new HashMap<>();
pictureSubMap.put((int) col1, pictureData);
pictureMap.put(row, pictureSubMap);
}
}
}
}
return pictureMap;
}
}
测试文件

接口测试

测试结果

感谢您阅读,欢迎您给出合理的意见

浙公网安备 33010602011771号