【193】Java8调用POI 5.2.5生成带图片的Excel文件

本文假定 Excel 文件中保存的是员工数据,并且数据中带有员工的头像。代码支持的图片格式有png、bmp、jpg、gif。但是这里需要注意,有些网站上下载的图片虽然后缀名是 jpg,但是文件二进制内容的格式是 WebP 的。Java8 目前官方api不支持 WebP ,本文不涉及webp相关话题,本文代码也不支持 WebP 格式。

另外我还遇到个坑,POI 5.2.5 在处理部分 jpg 格式图片的时候,无法把图片输出到 Excel 文件。为了解决这个问题,我在代码中把所有图片强行转成 png,保存为硬盘上的临时文件,再重新输出到 Excel 文件中。这个问题我没有在 POI 4.1.2 版本遇到过。

POI 的接口是线程不安全的,多个线程同时向一个文件输出会造成错误。如果读者想要在多线程环境(比如网站后端)使用下面的代码,要么使用锁,要么确保各个线程输出不同的文件。

本文的代码计算了图片的缩放比例,并且使用 picture.resize(scaleX, scaleY); 方法来设置图片缩放比例。这也与 POI 4.1.2 版本不同。POI 4.1.2 版本使用 picture.resize(1, 1); 会自动缩放图片调整成合适大小。

员工的 DTO 类:


/**
 * 员工DTO
 */
public class EmployeeDTO {
    // 工号
    private String no;

    // 姓名
    private String name;

    // 性别
    private String gender;

    // 头像
    private String portrait;


    @Override
    public String toString() {
        final StringBuffer sb = new StringBuffer("Employee{");
        sb.append("no='").append(no).append('\'');
        sb.append(", name='").append(name).append('\'');
        sb.append(", gender='").append(gender).append('\'');
        sb.append(", portrait='").append(portrait).append('\'');
        sb.append('}');
        return sb.toString();
    }

    public String getNo() {
        return no;
    }

    public void setNo(String no) {
        this.no = no;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getGender() {
        return gender;
    }

    public void setGender(String gender) {
        this.gender = gender;
    }

    public String getPortrait() {
        return portrait;
    }

    public void setPortrait(String portrait) {
        this.portrait = portrait;
    }
}

用来生成Excel 文件的 ImageExcelUtils 类


import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.List;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.CellStyle;
import org.apache.poi.ss.usermodel.ClientAnchor;
import org.apache.poi.ss.usermodel.CreationHelper;
import org.apache.poi.ss.usermodel.Drawing;
import org.apache.poi.ss.usermodel.Font;
import org.apache.poi.ss.usermodel.HorizontalAlignment;
import org.apache.poi.ss.usermodel.Picture;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.VerticalAlignment;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.util.IOUtils;
import org.apache.poi.xssf.streaming.SXSSFWorkbook;

import javax.imageio.ImageIO;

public class ImageExcelUtils {
    /**
     * 读取jpg图片
     * @param path 图片文件路径
     * @return BufferedImage
     */
    public static BufferedImage readBufferedImage(String path) {
        BufferedImage originImage = null;
        BufferedImage result = null;
        try {
            File file = new File(path);
            originImage = ImageIO.read(file);
            // 确保图片颜色只有RGB,没有alpha透明度
            result = new BufferedImage(
                    originImage.getWidth(),
                    originImage.getHeight(),
                    BufferedImage.TYPE_INT_RGB
            );
            result.getGraphics().drawImage(originImage, 0, 0, null);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return result;
    }


    /**
     * 设置excel文件的图片
     * @param workbook 工作簿
     * @param sheet sheet页
     * @param imgFilePath 图片文件路径
     * @param row1 图片起始的行
     * @param col1 图片起始的列
     * @param tempFilePath 临时文件路径
     */
    public static void setExcelImg(Workbook workbook, Sheet sheet, String imgFilePath,
                                   int row1, int col1, int width, int height, String tempFilePath) {
        File file = new File(imgFilePath);
        if (!file.exists()) {
            return;
        }
        // 临时文件
        File tempFile = new File(tempFilePath);
        if (tempFile.exists()) {
            tempFile.delete();
        }

        FileInputStream inputStream = null;
        boolean isClose = false;
        try {
            BufferedImage bufferedImage = readBufferedImage(imgFilePath);
            int imageWidth = bufferedImage.getWidth();
            int imageHeight = bufferedImage.getHeight();
            ImageIO.write(bufferedImage, "png", tempFile);

            inputStream = new FileInputStream(tempFile);
            //利用POI提供的工具类把文件流转化成二进制数据
            byte[] bytes = IOUtils.toByteArray(inputStream);
            //向POI内存中添加一张图片,返回图片在图片集合中的索引
            int pictureIndex = workbook.addPicture(bytes, Workbook.PICTURE_TYPE_PNG);//参数一:图片的二进制数据,参数二:图片类型
            //从Workbook中得到绘制图片的工具类
            CreationHelper helper = workbook.getCreationHelper();
            //创建锚点,设置图片坐标
            ClientAnchor clientAnchor = helper.createClientAnchor();
            clientAnchor.setAnchorType(ClientAnchor.AnchorType.DONT_MOVE_AND_RESIZE);
            clientAnchor.setRow1(row1); // 设置起始行
            clientAnchor.setCol1(col1); // 设置起始列

            //从sheet对象中得到一个绘图对象
            Drawing<?> drawingPatriarch = sheet.createDrawingPatriarch();
            //绘制图片,锚点,图片在内存中的位置
            Picture picture = drawingPatriarch.createPicture(clientAnchor, pictureIndex);

            // 使用固定的长宽比例系数
            double scaleX = 1.0;
            double scaleY = 1.0;
            if (imageWidth <= width && imageHeight <= height) {
                scaleX = 1.0;
                scaleY = 1.0;
            } else {
                scaleX = (double) width / (double) imageWidth;
                scaleY = (double) height / (double) imageHeight;
                double min = Math.min(scaleX, scaleY);
                scaleX = scaleY = min;
            }
            picture.resize(scaleX, scaleY);//自适应渲染图片
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (inputStream != null && !isClose) {
                    inputStream.close();
                }
                tempFile.delete();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 创建Excel文件
     * @param employeeDTOList 员工列表
     * @param file 输出的 Excel 文件
     * @param tempFilePath 临时文件路径
     */
    public static void createExcelFile(List<EmployeeDTO> employeeDTOList, File file, String tempFilePath) {
        //创建工作簿,excel2007版本的,如果是excel2003的话。创建的对象是:HSSFWorkbook
        Workbook workbook = new SXSSFWorkbook();
        //创建sheet
        Sheet sheet = workbook.createSheet("picture sheet");
        // 列宽分别是25个字符和40个字符,一字符等于 6.107 像素
        sheet.setColumnWidth(0, 25 * 256);
        sheet.setColumnWidth(1, 25 * 256);
        sheet.setColumnWidth(2, 25 * 256);
        sheet.setColumnWidth(3, 40 * 256);
        // 行高是 120 磅,1磅是 1.34039 像素
        sheet.setDefaultRowHeightInPoints(120f);

        // 设置字体,黑体
        Font font = workbook.createFont();
        font.setFontName("黑体");
        // 字体加粗
        font.setBold(true);
        CellStyle cellStyle = workbook.createCellStyle();
        cellStyle.setFont(font);
        cellStyle.setVerticalAlignment(VerticalAlignment.CENTER);
        cellStyle.setAlignment(HorizontalAlignment.CENTER);

        // 表格标题行
        Row firstRow = sheet.createRow(0);
        Cell r0c0 = firstRow.createCell(0);
        r0c0.setCellValue("工号");
        Cell r0c1 = firstRow.createCell(1);
        r0c1.setCellValue("姓名");
        Cell r0c2 = firstRow.createCell(2);
        r0c2.setCellValue("性别");
        Cell r0c3 = firstRow.createCell(3);
        r0c3.setCellValue("头像");

        r0c0.setCellStyle(cellStyle);
        r0c1.setCellStyle(cellStyle);
        r0c2.setCellStyle(cellStyle);
        r0c3.setCellStyle(cellStyle);

        int size = employeeDTOList.size();
        for (int i = 0; i < size; i++) {
            EmployeeDTO dto = employeeDTOList.get(i);
            int rowIndex = i + 1;
            Row row = sheet.createRow(rowIndex);
            Cell c0 = row.createCell(0);
            Cell c1 = row.createCell(1);
            Cell c2 = row.createCell(2);
            Cell c3 = row.createCell(3);

            // 向excel中输入图片
            String portrait = dto.getPortrait();
            if (null != portrait && portrait.trim().length() > 0) {
                portrait = portrait.trim();
                setExcelImg(workbook, sheet, portrait, rowIndex, 3, 244, 160, tempFilePath);
            }

            c0.setCellValue(dto.getNo());
            c1.setCellValue(dto.getName());
            c2.setCellValue(dto.getGender());
        }

        //创建文件输入流
        FileOutputStream out = null;
        //创建文件输出流
        try {
            out = new FileOutputStream(file);
            //调用工作簿的write创建excel
            workbook.write(out);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (out != null) {
                    out.flush();
                    out.close();
                    out = null;
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        } // end finally

    }
}

包含 main 方法的 Test 类,测试具体效果


import java.io.File;
import java.util.List;
import java.util.ArrayList;

public class Test {
    public static void main(String[] args) {
        List<EmployeeDTO> employeeDTOList = new ArrayList<>();

        EmployeeDTO emp_1 = new EmployeeDTO();
        emp_1.setNo("1");
        emp_1.setName("张三");
        emp_1.setGender("男");
        emp_1.setPortrait("D:/1715284526222417922.jpg");

        EmployeeDTO emp_2 = new EmployeeDTO();
        emp_2.setNo("2");
        emp_2.setName("李四");
        emp_2.setGender("男");
        emp_2.setPortrait("D:\\bmptest.bmp");

        EmployeeDTO emp_3 = new EmployeeDTO();
        emp_3.setNo("3");
        emp_3.setName("王二");
        emp_3.setGender("女");
        emp_3.setPortrait("D:\\113.jpg");


        EmployeeDTO emp_4 = new EmployeeDTO();
        emp_4.setNo("4");
        emp_4.setName("涂军");
        emp_4.setGender("男");
        emp_4.setPortrait("D:\\309446533.gif");

        employeeDTOList.add(emp_1);
        employeeDTOList.add(emp_2);
        employeeDTOList.add(emp_3);
        employeeDTOList.add(emp_4);

        File file = new File("D:\\ws\\tmpdir\\out.xlsx");
        String tempFilePath = "D:\\ws\\tmpdir\\temp.png";
        ImageExcelUtils.createExcelFile(employeeDTOList, file, tempFilePath);
    }
}

你可能感兴趣的:(JAVA,excel,java)