使用EasyExcel加springboot框架实现excel批量导出数据带图片,不失真(附带测试数据)

技术栈

  • 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的具体使用可以参考这位博主的帖子,我也是参考的她的,写的很详细

Java ImageIO 类详解-CSDN博客

核心思路

  1. 图片处理:使用ImageIO通过正则表达式从HTML内容中或图片的URL字符串提取图片URL,并将其转换为字节数组。

  2. 单元格样式:自定义单元格样式处理器,动态调整单元格大小以适应图片。

  3. 图片插入:利用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);
    }
}

测试实现效果

访问接口地址,测试效果,自行调整


                
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值