【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;@Overridepublic 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的话。创建的对象是:HSSFWorkbookWorkbook workbook = new SXSSFWorkbook();//创建sheetSheet 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创建excelworkbook.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);}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/211707.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【代码随想录算法训练营-第四天】【链表】24,19, 面试题 02.07,142

24. 两两交换链表中的节点 第一遍-递归-小看了一下题解 思路&#xff1a; 读了两遍题目才理解…相邻节点的交换&#xff0c;这个操作很容易实现&#xff0c;但需要一个tmpNode因为是链表的题目&#xff0c;没开始思考之前先加了dummyNode&#xff0c;还真管用把dummyNode作为…

空气质量数据和气象数据

1、北京、上海、广州的空气质量数据和气象数据 要素如下&#xff1a; 逐日数据 时间跨度&#xff1a;2014.1.1-2022.3.31&#xff0c;共3012条数据 数据质量&#xff1a;98% 城市&#xff1a;只有北京、上海、广州 可以用作论文数据 数据来源&#xff1a;中国环境监测总站…

23. 合并 K 个升序链表 --力扣 --JAVA

题目 给你一个链表数组&#xff0c;每个链表都已经按升序排列。 请你将所有链表合并到一个升序链表中&#xff0c;返回合并后的链表。 解题思路 对每个链表的首节点进行比较&#xff0c;获取当前的最小节点&#xff1b;将每个阶段的最小节点进行链接&#xff1b; 代码展示 c…

亚马逊云科技re_Invent 2023产品体验:亚马逊云科技产品应用实践 国赛选手带你看Elasticache Serverless

抛砖引玉 讲一下作者背景&#xff0c;曾经参加过国内世界技能大赛云计算的选拔&#xff0c;那么在竞赛中包含两类&#xff0c;一类是架构类竞赛&#xff0c;另一类就是TroubleShooting竞赛&#xff0c;对应的分别为AWS GameDay和AWS Jam&#xff0c;想必也有朋友玩过此类竞赛&…

4.权限特权转移代码

核心文件用户文件引导文件 核心文件 ;------------------------新增--------------------------------; 本文件涉及了权限, 将使用调用门描述符来处理 低权限到高权限的转移;------------------------权限---------------------------- ;此文件延用上个CORE.asm. 并做出一些修…

[linux] 解压缩xz

在Linux命令行中解压缩.xz文件&#xff0c;你可以使用以下几种方法&#xff1a; 使用unxz工具&#xff1a; unxz filename.xz 这个命令会将filename.xz解压缩为一个同名的未压缩文件。如果原文件有其他的扩展名&#xff08;如.tar.xz&#xff09;&#xff0c;那么这个扩展名会被…

关于洛谷P1007最快的方法

P1007 独木桥 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn) 题目背景 战争已经进入到紧要时间。你是运输小队长&#xff0c;正在率领运输部队向前线运送物资。运输任务像做题一样的无聊。你希望找些刺激&#xff0c;于是命令你的士兵们到前方的一座独木桥上欣赏风景&#xf…

智能仪表板DevExpress Dashboard v23.1 - 支持自定义样式创建

使用DevExpress Analytics Dashboard&#xff0c;再选择合适的UI元素&#xff08;图表、数据透视表、数据卡、计量器、地图和网格&#xff09;&#xff0c;删除相应参数、值和序列的数据字段&#xff0c;就可以轻松地为执行主管和商业用户创建有洞察力、信息丰富的、跨平台和设…

STM32 配置TIM定时中断常用库函数

单片机学习&#xff01; 目录 ​编辑 1. 函数TIM_DeInit 2. 函数TIM_TimeBaseInit 配置时基单元 3. 函数TIM_TimeBaseStructInit 4. 函数TIM_Cmd 运行控制 5. 函数TIM_ITConfig 中断输出控制 6. 时基单元的时钟选择函数 6.1 函数TIM_InternalClockConfig 6.2 函数 TIM…

Configuring environment||ROS2环境配置

Goal: This tutorial will show you how to prepare your ROS 2 environment. Tutorial level: Beginner Time: 5 minutes ROS 2 relies on the notion &#xff08;concept&#xff09;of combining workspaces using the shell environment. “Workspace” is a ROS term …

C++进阶篇8---智能指针

一、引言 为什么需要智能指针&#xff1f; 在上一篇异常中&#xff0c;关于内存释放&#xff0c;我们提到过一个问题---当我们申请资源之后&#xff0c;由于异常的执行&#xff0c;代码可能直接跳过资源的释放语句到达catch&#xff0c;从而造成内存的泄露&#xff0c;对于这种…

C# Winform 日志系统

目录 一、效果 1.刷新日志效果 2.单独日志的分类 3.保存日志的样式 二、概述 三、日志系统API 1.字段 Debug.IsScrolling Debug.Version Debug.LogMaxLen Debug.LogTitle Debug.IsConsoleShowLog 2.方法 Debug.Log(string) Debug.Log(string, params object[]) …

数据结构之内部排序

目录 7-1 直接插入排序 输入格式: 输出格式: 输入样例: 输出样例: 7-2 寻找大富翁 输入格式: 输出格式: 输入样例: 输出样例: 7-3 PAT排名汇总 输入格式: 输出格式: 输入样例: 输出样例: 7-4 点赞狂魔 输入格式&#xff1a; 输出格式&#xff1a; 输入样例&a…

RabbitMQ在国内为什么没有那么流行?

MQ&#xff08;消息队列&#xff09;的世界。MQ&#xff0c;就像是一个巨大的邮局&#xff0c;负责在不同服务或应用间传递消息。它可以帮助我们解耦系统&#xff0c;提高性能&#xff0c;还能做到异步处理和流量削峰。 基本使用 RabbitMQ是一个开源的消息代理和队列服务器&a…

spring boot + uniapp 微信公众号 jsapi 支付

后端支付类 package com.ruoyi.coupon.payment;import com.google.gson.Gson; import com.ruoyi.coupon.payment.dto.PayParamJsapiDto; import com.ruoyi.coupon.payment.dto.RefundParam; import com.ruoyi.coupon.service.ICouponConfigService; import com.wechat.pay.jav…

FFmpeg抽取视频h264数据重定向

根据视频重定向技术解析中的 截获解码视频流的思路&#xff0c;首先需要解决如何输出视频码流的问题。 目前只针对h264码流进行获取&#xff0c;步骤如下&#xff1a; 打开mp4文件并创建一个空文件用于存储H264数据 提取一路视频流资源 循环读取流中所有的包(AVPacket),为…

redis中使用pipeline批量处理请求提升系统性能

在操作数据库时&#xff0c;为了加快程序的执行速度&#xff0c;在新增或更新数据时&#xff0c;可以通过批量提交的方式来减少应用和数据库间的传输次数&#xff1b;在redis中也有这样的技术实现批量处理&#xff0c;也就是管道——Pipeline。它也是通过批量提交数据的方式来实…

线程安全3--wait和notify

文章目录 wait and notify&#xff08;等待通知机制notify补充 wait and notify&#xff08;等待通知机制 引入wait notify就是为了能够从应用层面上&#xff0c;干预到多个不同线程代码的执行顺序&#xff0c;这里说的干预&#xff0c;不是影响系统的线程调度策略&#xff08…

uni-app应用设置 可以根据手机屏幕旋转进行 (横/竖) 屏切换

首先 我们打开项目的 manifest.json 在左侧导航栏中找到 源码视图 然后找到 app-plus 配置 在下面加上 "orientation": [//竖屏正方向"portrait-primary",//竖屏反方向"portrait-secondary",//横屏正方向"landscape-primary",//横屏…

第57天:django学习(六)

模版之过滤器 语法&#xff1a; {{obj|filter__name:param}} 变量名字|过滤器名称&#xff1a;变量 default 如果一个变量是false或者为空&#xff0c;使用给定的默认值。否则&#xff0c;使用变量的值。例如&#xff1a; {{ value|default:"nothing"}} length …