本文假定 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);
}
}