jeecgboot 使用apache poi excel导入带图片

首先还是先说说我的思路:

  • 1、先读取Excel的图片,使用map将图片对应的行位置和存储路径记录下来。
  • 2、正常读取Excel数据并和实体类进行转换。
  • 3、通过注解反射读取到图片字段。
  • 4、映射图片地址到该字段。

具体实现:

1、引入Apache POI 依赖​

复制代码
<dependency>
    <groupId>org.apache.poi</groupId>
    <artifactId>poi-ooxml</artifactId>
    <version>5.2.3</version>
</dependency>

​2、编写自定义注解,用于标记图片字段

复制代码
@Target(ElementType.FIELD)//该注解作用于字段上
@Retention(RetentionPolicy.RUNTIME)//运行时保留,保证在运行时通过反射拿到
public @interface ExcelImageField {
    
}

3、编写图片提取工具类( XSSFWorkbook 打开传入的 Excel 文件流,然后获取到第一个工作表,这里大家有多个可以循环去提取。再根据 sheet.createDrawingPatriarch()获取到绘图对象。通过绘图对象拿到所有图片,再通过锚点去获取图片位置,然后保存图片,记录行号与路径)

java 复制代码
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.*;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.*;
import java.util.*;

public class ExcelImageUtils {
    
    /**
     * 从Excel中提取图片并保存到指定目录
     * @param inputStream Excel文件流
     * @param saveDir 图片保存目录(如:/upload/images/)
     * @return Map<行号, 图片路径> 如:{1: "/images/abc.jpg", 2: "/images/def.jpg"}
     */
    public static Map<Integer, String> extractImages(InputStream inputStream, String saveDir) throws IOException {
        Map<Integer, String> imageMap = new HashMap<>();
        
        try (Workbook workbook = new XSSFWorkbook(inputStream)) {
            Sheet sheet = workbook.getSheetAt(0);
            XSSFDrawing drawing = (XSSFDrawing) sheet.createDrawingPatriarch();
            
            if (drawing != null) {
                // 创建图片保存目录
                Path dirPath = Paths.get(saveDir);
                if (!Files.exists(dirPath)) {
                    Files.createDirectories(dirPath);
                }
                
                // 处理所有图片
                for (XSSFShape shape : drawing.getShapes()) {
                    if (shape instanceof XSSFPicture) {
                        XSSFPicture picture = (XSSFPicture) shape;
                        XSSFClientAnchor anchor = picture.getPreferredSize();
                        
                        // 获取图片行号(Excel行号从0开始)
                        int rowNum = anchor.getRow1();
                        
                        // 生成唯一文件名
                        String fileName = UUID.randomUUID() + getImageExtension(picture.getPictureData());
                        Path imagePath = dirPath.resolve(fileName);
                        
                        // 保存图片
                        Files.write(imagePath, picture.getPictureData().getData());
                        
                        // 存储访问路径(如:/images/filename.jpg)
                        imageMap.put(rowNum, "/images/" + fileName);
                    }
                }
            }
        }
        return imageMap;
    }
    
    private static String getImageExtension(XSSFPictureData pictureData) {
        switch (pictureData.getPictureType()) {
            case Workbook.PICTURE_TYPE_JPEG: return ".jpg";
            case Workbook.PICTURE_TYPE_PNG: return ".png";
            default: return ".png";
        }
    }
}

4、编写反射工具,通过反射动态设置值字段

java 复制代码
  /**
     * 根據對象获取图片字段名
     *
     * @param clazz
     * @return
     */
    private String getImageFieldName(Class<?> clazz) {
        for (Field field : clazz.getDeclaredFields()) {
            if (field.isAnnotationPresent(ExcelImageField.class)) {
                return field.getName();
            }
        }
        log.warn("实体类 {} 未找到@ExcelImageField注解字段", clazz.getSimpleName());
        return null;
    }

5、编写完整的导入流程(这里不包含Excel正常导入数据,只是在原来已有的Excel导入加入对图片的处理逻辑)

java 复制代码
/**
     * 通过excel导入数据帶圖片
     *
     * @param request
     * @param response
     * @return
     */
    protected Result<?> importExcel2(HttpServletRequest request, HttpServletResponse response, Class<T> clazz) {
        MultipartHttpServletRequest multipartRequest = (MultipartHttpServletRequest) request;
        Map<String, MultipartFile> fileMap = multipartRequest.getFileMap();
        for (Map.Entry<String, MultipartFile> entity : fileMap.entrySet()) {
            // 获取上传文件对象
            MultipartFile file = entity.getValue();
            ImportParams params = new ImportParams();
            params.setTitleRows(2);
            params.setHeadRows(1);
            params.setNeedSave(true);

            // 1. 提前读取图片(這裡先读取流需要重置)
            Map<Integer, String> imageMap = new HashMap<>();
            try (InputStream tempStream = file.getInputStream()) {
                imageMap = ExcelImageUtils.extractImages(tempStream, "/upload/images/");
            } catch (Exception e) {
                log.warn("读取图片失败(可能无图片): " + e.getMessage());
            }

            try {
                List<T> list = ExcelImportUtil.importExcel(file.getInputStream(), clazz, params);


                // 2. 为每条数据设置图片路径
                for (int i = 0; i < list.size(); i++) {
                    // 计算对应Excel行号(标题行+表头行+数据偏移)
                    int excelRowNum = params.getTitleRows() + params.getHeadRows() + i;
                    if (imageMap.containsKey(excelRowNum)) {
                        // 使用反射设置字段值,后改用註解
                        //setFieldValue(list.get(i), "verificationResults", imageMap.get(excelRowNum));
                        String imageField = getImageFieldName(clazz);
                        if (imageField != null) {
                            setFieldValue(list.get(i), imageField, imageMap.get(excelRowNum));
                        }
                    }
                }


                //update-begin-author:taoyan date:20190528 for:批量插入数据
                long start = System.currentTimeMillis();
                service.saveBatch(list);
                //400条 saveBatch消耗时间1592毫秒  循环插入消耗时间1947毫秒
                //1200条  saveBatch消耗时间3687毫秒 循环插入消耗时间5212毫秒
                log.info("消耗时间" + (System.currentTimeMillis() - start) + "毫秒");
                //update-end-author:taoyan date:20190528 for:批量插入数据
                return Result.ok("文件导入成功!数据行数:" + list.size());
            } catch (Exception e) {
                //update-begin-author:taoyan date:20211124 for: 导入数据重复增加提示
                String msg = e.getMessage();
                log.error(msg, e);
                if(msg!=null && msg.indexOf("Duplicate entry")>=0){
                    return Result.error("文件导入失败:有重复数据!");
                }else{
                    return Result.error("文件导入失败:" + e.getMessage());
                }
                //update-end-author:taoyan date:20211124 for: 导入数据重复增加提示
            } finally {
                try {
                    file.getInputStream().close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return Result.error("文件导入失败!");
    }
相关推荐
qq_25183645743 分钟前
springboot vue3 开发实现 拼豆管理系统
java·开发语言·ai编程
caoerzhong1 小时前
JeeWMS 开源仓库管理系统全景解读:一套 Java WMS 如何把 WMS/OMS/BMS/TMS 装进同一个系统
java·开源
曹牧1 小时前
Java集合框架:List、ArrayList、Map 和 HashMap
java
顾城猿1 小时前
STOMP 协议
java
+VX:Fegn08952 小时前
计算机毕业设计|基于springboot + vue旅游管理系统(源码+数据库+文档)
java·开发语言·vue.js·spring boot·课程设计
需要8262 小时前
MySQL 索引 B+ 树与“查询为什么变慢了
java·spring boot·spring·spring cloud·tomcat
一 乐2 小时前
二手交易平台|基于springboot + vue二手交易平台(源码+数据库+文档)
java·数据库·vue.js·spring boot·小程序
万年咸鱼3 小时前
编译Java源程序
java
Wang's Blog3 小时前
Java 项目实战: 外卖平台-删除分类的关联校验与自定义业务异常
java·服务器·项目开发
hai_android3 小时前
Kotlin / Android 常用函数使用示例手册
android·java·kotlin