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("文件导入失败!");
    }
相关推荐
Scigar4 小时前
Idea的安装以及基础使用
java·intellij-idea·idea
寒山李白4 小时前
IntelliJ IDEA新版下载、安装、创建项目及Maven配置的教程(附安装包等)
java·maven·intellij-idea
刘一说4 小时前
深入浅出 Spring Boot 自动配置(Auto-Configuration):原理、机制与最佳实践
java·spring boot·后端
程序员小假4 小时前
我们来说一说什么是联合索引最左匹配原则?
java·后端
我命由我123454 小时前
PDFBox - PDF 页面坐标系、PDF 页面尺寸获取、PDF 页面位置计算
java·服务器·开发语言·笔记·后端·java-ee·pdf
ᐇ9594 小时前
Java 程序运行原理与内存模型解析
java·开发语言
sp424 小时前
试探构建一个简洁、清晰的 Java 日期 API
java·后端
stu_kk4 小时前
泛微Ecology9实现流程界面隐藏按钮
java·oa