这里导出excel需要使用阿里的EasyExcel组件,项目需要引入EasyExcel依赖,版本可自己选择,博主这里使用2.2.7版本
com.alibaba
easyexcel
2.2.7
说到多图片导出,先要说下EasyExel的单图片导出,单图片导出比较简单,
直接使用下EasyExel自带的类UrlImageConverter(Converter的子类)实现,ExcelModel设置如下:
@Data
@ContentRowHeight(20)
@HeadRowHeight(20)
@ColumnWidth(25)
@HeadStyle(horizontalAlignment = HorizontalAlignment.CENTER)
@ContentStyle(horizontalAlignment = HorizontalAlignment.CENTER)
public class ExcelDemo {
@ExcelProperty(value = "图片", converter = UrlImageConverter.class)
private URL imageUrl;
}
我们先来看一下UrlImageConverter的实现:
package com.alibaba.excel.converters.url;
import com.alibaba.excel.converters.Converter;
import com.alibaba.excel.enums.CellDataTypeEnum;
import com.alibaba.excel.metadata.CellData;
import com.alibaba.excel.metadata.GlobalConfiguration;
import com.alibaba.excel.metadata.property.ExcelContentProperty;
import com.alibaba.excel.util.IoUtils;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
public class UrlImageConverter implements Converter {
public UrlImageConverter() {
}
public Class supportJavaTypeKey() {
return URL.class;
}
public CellDataTypeEnum supportExcelTypeKey() {
return CellDataTypeEnum.IMAGE;
}
public URL convertToJavaData(CellData cellData, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) {
throw new UnsupportedOperationException("Cannot convert images to url.");
}
// 核心方法 读取图片url的io流
public CellData convertToExcelData(URL value, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) throws IOException {
InputStream inputStream = null;
CellData var6;
try {
inputStream = value.openStream();
byte[] bytes = IoUtils.toByteArray(inputStream);
var6 = new CellData(bytes);
} finally {
if (inputStream != null) {
inputStream.close();
}
}
return var6;
}
}
其实转化的方法很简单,只是读取图片url的io,写进Excel的cellData对象里,那多图片的转化只需要加个for循环一次读取转化就行里吧?我们来自己实现下多图片的转换器(实现easyexcel的扩展接口Converter)
import com.alibaba.excel.converters.Converter;
import com.alibaba.excel.enums.CellDataTypeEnum;
import com.alibaba.excel.metadata.CellData;
import com.alibaba.excel.metadata.GlobalConfiguration;
import com.alibaba.excel.metadata.property.ExcelContentProperty;
import com.alibaba.excel.util.IoUtils;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
/**
* @Description: easyecxel多图片导出到单元格转换工具类
* @Author guowang
* @Date: 2021/7/7 15:56
* @Version 1.0
*/
public class ListUrlConverterUtil implements Converter> {
@Override
public Class supportJavaTypeKey() {
return List.class;
}
@Override
public CellDataTypeEnum supportExcelTypeKey() {
/**
*这里记得枚举类型为IMAGE
*/
return CellDataTypeEnum.IMAGE;
}
@Override
public List convertToJavaData(CellData cellData, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) throws Exception {
return null;
}
@Override
public CellData convertToExcelData(List value, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) throws Exception {
// 这里进行对数据实体类URL集合处理
List data = new ArrayList<>();
// for 循环一次读取
for (URL url : value) {
InputStream inputStream = null;
try {
inputStream = url.openStream();
byte[] bytes = IoUtils.toByteArray(inputStream);
data.add(new CellData(bytes));
} catch (Exception e) {
throw new IOException("导出excel图片读取异常");
} finally {
if (inputStream != null){
inputStream.close();
}
}
}
// 这种方式并不能返回一个List,所以只好通过CellData cellData = new CellData(data);将这个list对象塞到返回值CellData对象的data属性中;
CellData cellData = new CellData(data);
cellData.setType(CellDataTypeEnum.IMAGE);
return cellData;
}
}
ExcelModel设置如下:
@Data
@ContentRowHeight(20)
@HeadRowHeight(20)
@ColumnWidth(25)
@HeadStyle(horizontalAlignment = HorizontalAlignment.CENTER)
@ContentStyle(horizontalAlignment = HorizontalAlignment.CENTER)
public class ExcelDemo {
@ExcelProperty(value = "图片", converter = ListUrlConverterUtil.class)
private List imageUrls;
}
图片统一大小返回, 这里需要实现CellWriteHandler 的afterCellDataConverted 方法,将 CellData 的 type 设置成 EMPTY ,这样 EasyExcel 不会帮忙填充该单元格的数据,然后实现 CellWriteHandler 的 afterCellDispose 方法,将图片信息填充上去
注意:每列的单元格图片张数不确定,单元格宽度需按照张数最多的长度来设置
import com.alibaba.excel.enums.CellDataTypeEnum;
import com.alibaba.excel.metadata.CellData;
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 org.apache.commons.collections.CollectionUtils;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.util.Units;
import java.util.ArrayList;
import java.util.List;
/**
* @Description: 图片信息修改拦截器
* @Author GreenArrow
* @Date: 2021/7/7 16:05
* @Version 1.0
*/
public class CustomImageModifyHandler implements CellWriteHandler {
private List repeats = new ArrayList<>();
// 单元格的图片最大张数(每列的单元格图片张数不确定,单元格宽度需按照张数最多的长度来设置)
private Integer maxDataSize = 0;
@Override
public void beforeCellCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Row row, Head head, Integer columnIndex, Integer relativeRowIndex, Boolean isHead) {
}
@Override
public void afterCellCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Cell cell, Head head, Integer relativeRowIndex, Boolean isHead) {
}
@Override
public void afterCellDataConverted(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, CellData cellData, Cell cell, Head head, Integer relativeRowIndex, Boolean isHead) {
// 在 数据转换成功后 不是头就把类型设置成空
if (isHead) {
return;
}
//将要插入图片的单元格的type设置为空,下面再填充图片
if(cellData.getImageValue()!=null||cellData.getData() instanceof ArrayList){
cellData.setType(CellDataTypeEnum.EMPTY);
}
}
@Override
public void afterCellDispose(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, List cellDataList, Cell cell, Head head, Integer relativeRowIndex, Boolean isHead) {
// 在 单元格写入完毕后 ,自己填充图片
if (isHead || CollectionUtils.isEmpty(cellDataList)) {
return;
}
Boolean listFlag = false;
ArrayList data = null;
Sheet sheet = cell.getSheet();
// 此处为ListUrlConverterUtil的返回值
if (cellDataList.get(0).getData() instanceof ArrayList){
data = (ArrayList) cellDataList.get(0).getData();
if (CollectionUtils.isEmpty(data)) {
return;
}
if (data.get(0) instanceof CellData){
CellData cellData = (CellData) data.get(0);
if (cellData.getImageValue() == null){
return;
}else {
listFlag = true;
}
}
}
if (!listFlag && cellDataList.get(0).getImageValue() == null){
return;
}
String key = cell.getRowIndex() + "_" + cell.getColumnIndex();
if (repeats.contains(key)){
return;
}
repeats.add(key);
if (data.size() > maxDataSize) {
maxDataSize = data.size();
}
// 默认要导出的图片大小为175*175px,175px的行高大约是900,175px列宽大概是25*256
sheet.getRow(cell.getRowIndex()).setHeight((short)1800);
sheet.setColumnWidth(cell.getColumnIndex(),listFlag?25*256*maxDataSize:25*256);
if (listFlag){
for (int i = 0; i < data.size(); i++) {
CellData cellData= (CellData) data.get(i);
if(cellData.getImageValue()==null){
continue;
}
this.insertImage(sheet,cell,cellData.getImageValue(),i);
}
}else {
// cellDataList 是list的原因是 填充的情况下 可能会多个写到一个单元格 但是如果普通写入 一定只有一个
this.insertImage(sheet,cell,cellDataList.get(0).getImageValue(),0);
}
}
private void insertImage(Sheet sheet,Cell cell,byte[] pictureData,int i){
int picWidth = Units.pixelToEMU(175);
int index = sheet.getWorkbook().addPicture(pictureData, HSSFWorkbook.PICTURE_TYPE_PNG);
Drawing drawing = sheet.getDrawingPatriarch();
if (drawing == null) {
drawing = sheet.createDrawingPatriarch();
}
CreationHelper helper = sheet.getWorkbook().getCreationHelper();
ClientAnchor anchor = helper.createClientAnchor();
// 设置图片坐标
anchor.setDx1(picWidth*i);
anchor.setDx2(picWidth+picWidth*i);
anchor.setDy1(0);
anchor.setDy2(0);
//设置图片位置
anchor.setCol1(cell.getColumnIndex());
anchor.setCol2(cell.getColumnIndex());
anchor.setRow1(cell.getRowIndex());
anchor.setRow2(cell.getRowIndex() + 1);
// 设置图片可以随着单元格移动
anchor.setAnchorType(ClientAnchor.AnchorType.MOVE_AND_RESIZE);
drawing.createPicture(anchor, index);
}
}
使用EasyExcel的write方法将数据写到excel中,这里比较重要的是需要.registerWriteHandler,这里需要将自己写的图片拦截器注入进去
EasyExcel.write(outputStream, ExcelDemo.class)
.registerWriteHandler(new CustomImageModifyHandler()).sheet().doWrite(查询出来并且完成数据转换的Excel数据集合)