在日常的工作中,有时我们会遇到需要将数据库表中图片字段下载到Excel中的需求,为方便各位小伙伴今后的开发工作,我将分享出自己写的代码,并指出图片下载到Excel文件中的性能瓶颈问题。 在文章末尾有我上传到 Gitee 上的 Demo案例,希望大家喜欢。
将图片下载到Excel文件中并导出,先看一下效果。
1、使用的 POI 依赖
需要说明的是: commons-io 依赖必须有,不然在编写代码的过程中会异常。由于 POI 组件版本升级的问题,本文分享的代码适合 3.17 版本的依赖,版本太高的jar包可能不适合。
在我自己学习、测试过程中,5.2.0版本的POI组件Jar包就不适合本文的代码。
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.11.0</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
<version>3.17</version>
<!--<version>5.2.0</version>-->
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>3.17</version>
<!--<version>5.2.0</version>-->
</dependency>
2、关键部分代码展示
在以下的函数中,大体分为以下几步:
2-1、查询获取结果值:userVOList ,并将其转成导出 exportVOS ;
2-2、创建工作簿对象workbook,并因此创建工作表对象 sheet ;
2-3、对工作表 sheet工作表对象 设置头部标题数据、头部标题样式;
2-4、对 sheet工作表对象 填充数据,这些数据分为普通文本数据、图片数据。
2-5、将工作簿对象 workbook 输出。
/***
* 用户信息 导出
*
* @param paramDTO 用户入参DTO
* @param response response请求
*
* @return
* @author moon 2023/6/27 10:31
*/
@Override
public void exportUser(UserParamDTO paramDTO, HttpServletResponse response) throws Exception {
// 1-1、获取列表查询数据
List<UserVO> userVOList = userMapperExt.selectUserByParamDTO(paramDTO);
// 1-2、将 UserVO 转成 UserExportVO
List<UserExportVO> exportVOS = userVOList.stream().map(infoVO -> BeanUtils.convert(infoVO, UserExportVO.class)).collect(Collectors.toList());
//空数据导出不了标题 需要放一个空对象
if (CollectionUtils.isEmpty(exportVOS)) {
exportVOS = new ArrayList<>();
}
// Map<'英文名', '中文名'> 组合形式.
Map<String, String> aliasMap = ExcelUtils.getDTOAliasTitle(UserExportVO.class, Constant.BooleanFlag.FALSE);
List<String> headerList = new ArrayList<>(aliasMap.keySet());
// 2-1、创建 工作簿 对象.
Workbook workbook = new XSSFWorkbook();
// 2-2、创建工作表对象
Sheet sheet = workbook.createSheet();
// 设置标题行,第0行.
Row headerRow = sheet.createRow(Constant.Digital.ZERO);
// 获取头部单元格样式
CellStyle headerCellStyle = ExcelUtils.getCellStyle(workbook, HorizontalAlignment.CENTER, VerticalAlignment.CENTER, Constant.Digital.ONE);
// 2-3、设置头部单元格样式
for (int i = 0; i < headerList.size(); i++) {
// 获取中文值
String value = aliasMap.get(headerList.get(i));
Cell cell = headerRow.createCell(i);
// 设置 样式
cell.setCellStyle(headerCellStyle);
// 设置 值
cell.setCellValue(value);
}
// 2-4、填充单元格数据
for (int i = 0; i < exportVOS.size(); i++) {
// 创建行对象,从第1行开始。
int rowNum = i + 1;
Row row = sheet.createRow(rowNum);
UserExportVO tempVO = exportVOS.get(i);
// 创建单元格对象(列)并设置值
for (int j = 0; j < headerList.size(); j++) {
Cell cell = row.createCell(j);
Field field = UserExportVO.class.getDeclaredField(headerList.get(j));
// 若属性为私有,需要设置可访问。
field.setAccessible(Constant.BooleanFlag.TRUE);
String fieldName = field.getName();
String fieldValue;
if (Objects.isNull(field.get(tempVO))) {
fieldValue = Constant.CommonlySymbols.EMPTY;
} else {
fieldValue = field.get(tempVO).toString();
}
if (StringUtils.equalsAny(fieldName, UserVO.FIELD_HEAD_PHOTO, UserVO.FIELD_SCHOOL_PHOTO)
&& StringUtils.isNotBlank(fieldValue)) { // 2个图片字段,需要特殊处理.
// 构造 URL
String path = field.get(tempVO).toString();
URL url = new URL(path);
// 打开连接
URLConnection con = url.openConnection();
//设置请求超时为5s
con.setConnectTimeout(8 * 1000);
// 输入流
InputStream is = con.getInputStream();
byte[] bytes = IOUtils.toByteArray(is);
@SuppressWarnings("static-access")
int pictureIdx = workbook.addPicture(bytes, workbook.PICTURE_TYPE_PNG); // 添加图片到工作簿中
CreationHelper helper = workbook.getCreationHelper(); // 获取创建帮助类
Drawing drawing = sheet.createDrawingPatriarch(); // 获取绘图父对象
ClientAnchor anchor = helper.createClientAnchor(); // 创建客户端锚点
// 图片插入坐标
anchor.setCol1(j); // 设置图片左上角的列数
anchor.setRow1(rowNum); // 设置图片左上角的行数
// 插入图片
Picture pict = drawing.createPicture(anchor, pictureIdx); // 在指定位置插入图片
// 设置 单元格高度
cell.getRow().setHeight((short) 1000); // 将单元格所在行的高度设置为1000
// 设置 单元格宽度
sheet.setColumnWidth(cell.getColumnIndex(), 2600); // 将单元格所在列的宽度设置为2600
// 设置图片宽、高放缩比例
pict.resize(1, 1); // 这行代码,可以将值设置成 pict.resize(0.5, 0.5)、pict.resize(2, 2),看看效果如何。
} else {
cell.setCellValue(fieldValue);
}
}
}
// 将工作簿写入文件中
try {
response = ExcelUtils.getHttpServletResponse(response, Constant.ExcelProperty.USER_EXPORT_NAME);
ServletOutputStream outputStream = response.getOutputStream();
workbook.write(outputStream);
} finally {
workbook.close();
}
}
由于数据库表中存在的是图片的URL地址,在获取图片信息时,需要逐个调用URL获取数据,当图片数据量较大时,会出现超时问题、网络IO问题,这也是图片下载的瓶颈所在。
3、使用Postman进行测试
3-1、测试的URL:http://localhost:8080/user/exportUser
3-2、入参参数:
{
"age": "",
"createDate": "",
"distinct": true,
"id": "",
"isDeleted": "",
"name": ""
}
3-3、返回结果:
在之后的时间里,我将还会分享 Excel勾选导出 代码案例,希望能够帮到大家。
Gitee上的Demo案例: apache-poi-demo ,项目名称:apache-poi-demo .
入口函数:com.moon.poi.service.impl.UserServiceImpl#exportUser(...)