poi + 自定义注解操作Excel读取图片

背景

1:你excel是不是用挺多的
2:还好,阿里那套用的多
1:那你有知道怎么读取图片吗,像下面这种
在这里插入图片描述
2:不知道,自己百度
1:百度都是用原始poi写的,没有工具啊,好复杂又不能通用,你有没有工具
2:没有,不想搞
1:请你吃饭,弄一个呗
2:行吧,试试整一个

说明

废话不多说,下面将直接放代码,缺少包请自行下载
代码可以直接复制使用
有问题可以评论留言,看到就回

代码

这里用的是EasyExcel的ExcelProperty注解,你也可以自己定义注解

ExcelUtils工具

    /**
     * 读取,可读取图片,需调用imgCallback()方法对图片进行处理并返回路径(不支持CSV文件导入,因为csv暂时无法处理图片)
     *
     * @param stream       输入流
     * @param callBack     imgCallback()方法图片处理后返回的路径
     * @param headRowCount 表头的行数(比如有些表格存在多行头,T为bean对象时无效,为Map时生效),
     *                     (当T为Map时, headRowCount不传默认为1,第一行是表头; 当T为bean对象时根据ExcelProperty配置字段计算)
     * @param heads        默认null,重新设置表头,且clazz的表头失效,只对T为bean对象生效,(特别注意: heads的表头顺序与clazz的字段顺序必须保持一致,否则bean赋值可能出错)
     */
    public static <T> List<T> excelImageRead(InputStream stream, Class<T> clazz, Integer headRowCount, List<List<String>> heads, ImageCallBack callBack) throws Exception {
        Workbook workbook = WorkbookFactory.create(stream); // 支持2003版本和2007版本
        Sheet sheet = workbook.getSheetAt(0); // 只处理第一个表
        // 如果是SXSSFWorkbook类型,转XSSFWorkbook类型,否则sheet.getRow可能为空
        if (workbook instanceof SXSSFWorkbook) {
            SXSSFWorkbook sxssfWorkbook = (SXSSFWorkbook) workbook;
            sheet = sxssfWorkbook.getXSSFWorkbook().getSheetAt(workbook.getSheetIndex(sheet));
        }
        ExcelTools.handleExcelImage(sheet, callBack); // 处理图片
        return ExcelTools.saveRowData(sheet, clazz, headRowCount, heads); // 存储并返回数据
    }

自定义ExcelTools工具


public class ExcelTools {
    /**
     * 处理图片
     *
     * @param sheet    excel表
     * @param callBack 回调处理
     */
    public static void handleExcelImage(Sheet sheet, ImageCallBack callBack) throws Exception {
        if (Objects.nonNull(callBack) && Objects.nonNull(sheet.getDrawingPatriarch())) {
            for (Shape shape : sheet.getDrawingPatriarch()) {
                ClientAnchor anchor = (ClientAnchor) shape.getAnchor();
                int row1 = anchor.getRow1();   // 获取行编号: getRow2():获取图片右下角行号 getRow1():获取图片左上角行号
                short col1 = anchor.getCol1();// 获取列编号: getCol2():获取图片右下角列号 getCol1():获取图片左上角列号

                if (row1 > sheet.getLastRowNum()) {
                    throw new Exception("图片位置错误(" + row1 + "," + col1 + ")");
                }
                if (shape instanceof Picture) {
                    Picture picture = (Picture) shape;
                    PictureData pictureData = picture.getPictureData();
                    String url = callBack.imgCallback(pictureData); // 调用回调处理

                    Row row = Objects.isNull(sheet.getRow(row1)) ? sheet.createRow(row1) : sheet.getRow(row1);
                    Cell cell = Objects.isNull(row.getCell(col1)) ? row.createCell(col1) : row.getCell(col1);
                    List<String> urls;
                    if (StringUtils.isNotBlank(cell.getStringCellValue())) {
                        try {
                            urls = JsonUtils.getJsonToList(cell.getStringCellValue(), String.class);
                        } catch (Exception e) {
                            urls = new ArrayList<>();
                        }
                    } else {
                        urls = new ArrayList<>();
                    }
                    urls.add(url);
                    cell.setCellValue(JsonUtils.getBeanToJson(urls));
                }
            }
        }
    }

    /**
     * 保存并返回数据
     *
     * @param headRowCount 表头的行数(比如有些表格存在多行的头,T为bean对象时无效,为Map时生效), (当T为Map时, headRowCount不传默认为1,第一行是表头; 当T为bean对象时根据ExcelProperty配置字段计算)
     * @param newHeads     默认null,重新设置表头,且clazz的表头失效,只对T为bean对象生效,(特别注意: heads的表头顺序与clazz的字段顺序必须保持一致,否则bean赋值可能出错)
     */
    public static <T> List<T> saveRowData(Sheet sheet, Class<T> clazz, Integer headRowCount, List<List<String>> newHeads) throws Exception {
        List<T> list = new ArrayList<>();
        Map<Integer, List<String>> cellHeadMap = new HashMap<>(); // 列-头
        Map<String, Field> headFieldMap = getHeadFieldMap(clazz, newHeads); // 头-字段

        // 多行头数(存在多行头)
        int headCount;
        if (Objects.isNull(headFieldMap)) {
            headCount = Objects.isNull(headRowCount) ? 1 : headRowCount;
        } else {
            headCount = headFieldMap.keySet().stream().map(k -> JsonUtils.getJsonToList(k, String.class)).filter(Objects::nonNull).map(List::size).reduce(Integer::max).orElse(1);
        }

        // 分析每一行
        for (int i = 0; i <= sheet.getLastRowNum(); i++) {
            Row row = sheet.getRow(i);
            if (Objects.isNull(row)) continue;

            T t = null; // 声明对象
            boolean existContent = false; // 存在内容
            int rowNum = row.getRowNum();  // 从0开始

            // headCount行以内都是表头
            Iterator<Cell> cellIterator = row.cellIterator();
            while (cellIterator.hasNext() && rowNum < headCount) {
                Cell cell = cellIterator.next();
                DataFormatter dataFormatter = new DataFormatter();
                String val = dataFormatter.formatCellValue(cell);
                List<String> heads = cellHeadMap.getOrDefault(cell.getColumnIndex(), new ArrayList<>());
                heads.add(val);
                cellHeadMap.put(cell.getColumnIndex(), heads);
            }

            if (rowNum >= headCount) {
                int hs = cellHeadMap.size(); // 头数量
                for (int cn = 0; cn < hs; cn++) {
                    t = Objects.isNull(t) ? clazz.getDeclaredConstructor().newInstance() : t;

                    Cell cell = row.getCell(cn, Row.MissingCellPolicy.CREATE_NULL_AS_BLANK);
                    List<String> heads = cellHeadMap.get(cell.getColumnIndex()); // 获取表头字段名称

                    DataFormatter dataFormatter = new DataFormatter();
                    String val = dataFormatter.formatCellValue(cell);
                    if (StringUtils.isBlank(val)) {
                        continue;
                    }

                    // bean
                    if (Objects.nonNull(headFieldMap)) {
                        Field field1 = headFieldMap.get(JsonUtils.getBeanToJson(heads)); // 为bean时获取表头对应的bean字段
                        if (Objects.nonNull(field1)) {
                            field1.set(t, val); // 为字段赋值
                            existContent = StringUtils.isNotBlank(val) || existContent; // 内容是否存在
                        }
                    }

                    // map
                    if (Map.class.isAssignableFrom(clazz)) {
                        Method put = clazz.getMethod("put", Object.class, Object.class);
                        put.invoke(t, String.join(",", heads), val);
                        existContent = StringUtils.isNotBlank(val) || existContent; // 内容是否存在
                    }
                }
            }

            // 收集数据
            if (Objects.nonNull(t)) {
                list.add(t);
            }
        }
        return list;
    }

    /**
     * 获取map<excel头,字段>
     */
    private static <T> Map<String, Field> getHeadFieldMap(Class<T> clazz, List<List<String>> heads) {
        if (Map.class.isAssignableFrom(clazz)) {
            return null;
        }
        // 存储注解位置-字段
        Map<String, Field> indexMap = new HashMap<>(); // 字段-对象属性
        Field[] fields = clazz.getDeclaredFields();

        // 新表头
        if (!CollectionUtils.isEmpty(heads)) {
            // 取表头行数
            int max = heads.stream().map(List::size).reduce(Integer::max).orElse(0);

            for (int i = 0; i < heads.size(); i++) {
                List<Field> fs = Arrays.stream(fields).filter(field -> field.isAnnotationPresent(ExcelProperty.class)).collect(Collectors.toList());

                List<String> value = new ArrayList<>(heads.get(i));
                // 不足时最后一个往后填充
                while (value.size() < max) {
                    value.add(value.get(value.size() - 1));
                }
                if (i < fs.size()) {
                    Field field = fs.get(i);
                    if (field.isAnnotationPresent(ExcelProperty.class)) {
                        ExcelProperty excelProperty = field.getAnnotation(ExcelProperty.class);
                        System.out.println(excelProperty);
                        field.setAccessible(true); // 设置私有字段可赋值
                        indexMap.put(JsonUtils.getBeanToJson(value), field);
                    }
                }
            }
        } else {
            // 取表头行数
            int max = Arrays.stream(fields).filter(field -> field.isAnnotationPresent(ExcelProperty.class))
                    .map(field -> field.getAnnotation(ExcelProperty.class).value().length).reduce(Integer::max).orElse(0);

            for (Field field : fields) {
                if (field.isAnnotationPresent(ExcelProperty.class)) {
                    field.setAccessible(true); // 设置私有字段可赋值
                    ExcelProperty excelProperty = field.getAnnotation(ExcelProperty.class);
                    // 没设置值或者设了一个空串时按字段名称处理
                    int vl = excelProperty.value().length;
                    List<String> value = vl == 0 || vl == 1 && "".equals(excelProperty.value()[0]) ? Stream.of(field.getName()).collect(Collectors.toList())
                            : Stream.of(excelProperty.value()).collect(Collectors.toList());
                    // 不足时最后一个往后填充
                    while (value.size() < max) {
                        value.add(value.get(value.size() - 1));
                    }
                    indexMap.put(JsonUtils.getBeanToJson(value), field);
                }
            }
        }
        return indexMap;
    }
}

ImageCallBack 回调

/**
 * 图片回调
 */
public interface ImageCallBack {
    /**
     * 导入或上传或读excel时处理:图片回调
     *
     * @param pictureData 图片数据
     * @return 图片处理后的路径
     */
    String imgCallback(PictureData pictureData) throws Exception;
}

测试

    @GetMapping("/test2")
    @ApiOperation(value = "测试excel图片导入", notes = "测试excel图片导入")
    public JsonReturn test2(@RequestParam("file") MultipartFile file) throws Exception {
        List<User> users = ExcelUtils.excelImageRead(file.getInputStream(), User.class, 1, null, pictureData -> "此处处理图片逻辑,如上传后返回路径等");
        return JsonReturn.ok(users);
    }
    
    @GetMapping("/test3")
    @ApiOperation(value = "测试excel图片导入", notes = "测试excel图片导入")
    public JsonReturn test3(@RequestParam("file") MultipartFile file) throws Exception {
        List<List<String>> heads = Stream.of("A", "B", "C", "D", "E", "F", "G").map(Collections::singletonList).collect(Collectors.toList()); //此处可以自定义表头,则User表头注解失效
        List<User> users = ExcelUtils.excelImageRead(file.getInputStream(), User.class, 1, heads, pictureData -> "此处处理图片逻辑");
        return JsonReturn.ok(users);
    }


/**
 * 测试可用的对象,可删
 */
@HeadStyle(fillForegroundColor = 1)
public class User implements Serializable {
    private static final long serialVersionUID = 1L;
    @ExcelProperty(value = {"商品名称"})
    private String name;

    @ExcelProperty(value = {"商品图片"})
    private String img;
    
    //get set方法自己写。。。
}

写完了,又不想写了,写文章真累啊!!!!

相关内容:
https://blog.csdn.net/jingxin_123/article/details/138522876

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值