## 通过POI快速导入带图片的excel并且图片不会丢失
导入带图片的excel,这里也是研究了很久,在之前的博客中也有说明过,在项目使用过程中,发现很多时候导入响应很慢,而且每次导入图片都会丢失几张,所以又花了点时间研究修改了下,具体如下:
这边在导入时,通过自定义注解,将excel读取到的数据转换到需要的实体类中,然后进行后续处理。
接下来看下面说明:
-
excel模板如下:
-
导入poi依赖
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>4.1.2</version>
</dependency>
-
接口如下
@PostMapping("/import")
public CommonResult importData(@RequestParam("file") MultipartFile[] files){
MultipartFile file = files[0];
String fileName = file.getOriginalFilename();
// 上传文件为空
if (StringUtils.isEmpty(fileName)) {
throw new ServiceException("没有导入文件");
}
// 上传文件名格式不正确
if (fileName.lastIndexOf(".") != -1 && !".xlsx".equals(fileName.substring(fileName.lastIndexOf(".")))) {
throw new ServiceException( "文件名格式不正确, 请使用后缀名为.xlsx的文件");
}
dataService.importData(file);
return CommonResult.success();
}
这里的CommonResult是自定义的返回类,ServiceException自定义异常,dataService是实现方法
-
实现方法如下:
@Override
public void importData(MultipartFile file) {
ExcelImportByPicture<DetailsDto> testImport = new ExcelImportByPicture(DetailsDto.class);
List<DetailsDto> detailsDtos;
try {
detailsDtos= testImport.readExcelImageAndData(file, Constants.THREE);
} catch (Exception e) {
log.error("导入数据解析流转为DetailsDto失败:{}", e.getMessage());
throw new ServiceException("导入数据文件解析失败!");
}
//TODO 封装实体数据和文件上传
log.info("获取到的数据:{}", detailsDtos);
}
这里的ExcelImportByPicture工具类就是数据读取和处理的工具类,先读取数据,再转换成这里要映射的实体类,返回给detailsDtos。就能看到最终读取的数据。
这里的Constants.THREE = 3,也就是标题及其它信息所占用的行,没有标题,只有抬头行,直接传入0就可。可根据自己实际情况修改。
-
请求参数类:
@Data
public class DetailsDto implements Serializable {
/**
* 序号
*/
@Excel(name = "序号")
private String no;
/**
* 案件编号
*/
@Excel(name = "案件编号")
private String num;
/**
* 道路名称
*/
@Excel(name = "位置")
private String address;
/**
* 具体点位
*/
@Excel(name = "具体点位")
private String point;
/**
* 存在问题
*/
@Excel(name = "存在问题")
private String problem;
/**
* 病害来源
*/
@Excel(name = "问题来源")
private String source;
/**
* 发现时间
*/
@Excel(name = "发现时间")
private Date findTime;
/**
* 修复时间
*/
@Excel(name = "时间")
private Date time;
/**
* 维修面积
*/
@Excel(name = "大小")
private Double area;
/**
* 维护前照片
*/
@Excel(name = "照片1", getPicture = true)
private PictureData beforeimg;
/**
* 维护后照片
*/
@Excel(name = "照片2", getPicture = true)
private PictureData afterimg;
/**
* 备注
*/
@Excel(name = "备注")
private String remark;
private static final long serialVersionUID = 1L;
}
这里的@excel就是自定义的注解信息,name需要和模板中的抬头列名字一样,便于后面数据转换,getPicture表示为图片列。
-
自定义注解类
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface Excel
{
/**
* 导出到Excel中的名字.
*/
public String name() default "";
/**
* 日期格式, 如: yyyy-MM-dd
*/
public String dateFormat() default "";
/**
* 读取图片 true=是,false=否)
*/
public boolean getPicture() default false;
/**
* 当值为空时,字段的默认值
*/
public String defaultValue() default "";
/**
* 设置只能选择不能输入的列内容.
*/
public String[] combo() default {};
/**
* 自定义数据处理器参数
*/
public String[] args() default {};
/**
* 字段类型(0:导出导入;1:仅导出;2:仅导入)
*/
Type type() default Type.ALL;
public enum Type
{
ALL(0), EXPORT(1), IMPORT(2);
private final int value;
Type(int value)
{
this.value = value;
}
public int value()
{
return this.value;
}
}
}
这里可以定义你需要的注解参数,然后在工具类进行具体业务实现。
-
excel读取工具类:
public class ExcelImportByPicture<T> {
private static final Logger log = LoggerFactory.getLogger(ExcelImportByPicture.class);
/**
* 导出类型(EXPORT:导出数据;IMPORT:导入模板)
*/
private Excel.Type type;
/**
* 实体对象
*/
public Class<T> clazz;
public ExcelImportByPicture(Class<T> clazz) {
this.clazz = clazz;
}
/**
* @description: 获取excel上传中的图片及数据信息
* @param: [file, titleNum]
* @return: java.util.List<T>
* @date: 2023/11/9 15:51
* @version: 1.0
**/
public List<T> readExcelImageAndData(MultipartFile file, int titleNum) throws InstantiationException, IllegalAccessException {
// 读取上传excel
Workbook wb = readExcel(file);
//Workbook wb = WorkbookFactory.create(file.getInputStream());
if (wb == null) {
return null;
}
// 获取当前页,这里如果有多页时可以获取更多的页,通过循环获取每页的数据
// 获取总页数:wb.getNumberOfSheets(),获取某一个:wb.getSheet(sheetName)
Sheet sheet = wb.getSheetAt(Constants.ZERO);
//1:获取图片集合。行-列为key
Map<String, PictureData> pictureDataMap = readExcelPicture(sheet);
//2:获取excel中的数据(这里的数据不含图片信息)
// 将图片信息传入,再通过实体字段属性将每个数据映射到字段上,包括获取到的图片信息
return readExcelInfoAndPutClass(sheet, titleNum, pictureDataMap);
}
/**
* @description: 将图片信息传入,再通过实体字段属性将每个数据映射到字段上,包括获取到的图片信息
* @param: [sheet, titleNum, pictureDataMap]
* @return: java.util.List<T>
* @date: 2023/11/9 16:42
* @version: 1.0
**/
private List<T> readExcelInfoAndPutClass(Sheet sheet, int titleNum, Map<String, PictureData> pictureDataMap) throws InstantiationException, IllegalAccessException {
//存储实体list
List<T> list = new ArrayList<>();
//获取每个抬头及对应的实体字段进行映射到相应的下标上
Map<Integer, Object[]> fieldsMap = getFieldsMap(sheet, titleNum);
// 获取不为空的总行数
int rowSize = sheet.getPhysicalNumberOfRows();
// 遍历每一行,获取除了图片信息外的字段信息
for (int rowNum = titleNum + 1; rowNum < rowSize; rowNum++) {
Row row = sheet.getRow(rowNum);
if (isRowEmpty(row)) {
continue;
}
//建立所映射的实体对象
T entity = null;
for (Map.Entry<Integer, Object[]> entry : fieldsMap.entrySet()) {
Object val = row.getCell(entry.getKey());
// 如果不存在实例则新建.
entity = (entity == null ? clazz.newInstance() : entity);
// 从map中得到对应列的field.
Field field = (Field) entry.getValue()[0];
Excel attr = (Excel) entry.getValue()[1];
// 取得类型,并根据对象类型设置值.
Class<?> fieldType = field.getType();
//判断自定义属性并设置相关信息
putValByCustomAttribute(fieldType, field, attr, rowNum, val, entry, pictureDataMap, entity, row);
}
list.add(entity);
}
return list;
}
/**
* @description: 根据自定义属性设置相应的值
* @param: [fieldType, field, attr, rowNum, val, entry, pictureDataMap, entity]
* @return: void
* @date: 2023/11/9 16:20
* @version: 1.0
*/
private void putValByCustomAttribute(Class<?> fieldType, Field field, Excel attr,
int rowNum, Object val, Map.Entry<Integer, Object[]> entry,
Map<String, PictureData> pictureDataMap, T entity, Row row) {
//判断字段的类型来设置正确的值
if (String.class == fieldType) {
String s = Convert.toStr(val);
if (StringUtils.endsWith(s, ".0")) {
val = StringUtils.substringBefore(s, ".0");
} else {
String dateFormat = field.getAnnotation(Excel.class).dateFormat();
if (StringUtils.isNotEmpty(dateFormat)) {
val = parseDateToStr(dateFormat, val);
} else {
val = Convert.toStr(val);
if (ObjectUtils.isEmpty(val)) {
val = null;
}
}
}
} else if ((Integer.TYPE == fieldType || Integer.class == fieldType) && StringUtils.isNumeric(Convert.toStr(val))) {
val = Convert.toInt(val);
} else if ((Long.TYPE == fieldType || Long.class == fieldType) && StringUtils.isNumeric(Convert.toStr(val))) {
val = Convert.toLong(val);
} else if (Double.TYPE == fieldType || Double.class == fieldType) {
val = Convert.toDouble(val);
} else if (Float.TYPE == fieldType || Float.class == fieldType) {
val = Convert.toFloat(val);
} else if (BigDecimal.class == fieldType) {
val = Convert.toBigDecimal(val);
} else if (Date.class == fieldType) {
val = row.getCell(entry.getKey()).getDateCellValue();
} else if (Boolean.TYPE == fieldType || Boolean.class == fieldType) {
val = Convert.toBool(val, false);
}
if (null != fieldType) {
//自定义属性没有的话可以删除某一个属性判断
String propertyName = field.getName();
if (attr.getPicture()) {
String rowAndCell = rowNum + "-" + entry.getKey();
PictureData pictureData = pictureDataMap.get(rowAndCell);
if (Objects.nonNull(pictureData)) {
val = pictureData;
} else {
val = null;
}
}
ReflectUtils.invokeSetter(entity, propertyName, val);
}
}
/**
* @description: 获取每个抬头及对应的实体字段进行映射到相应的下标上
* @param: [sheet, titleNum]
* @return: java.util.Map<java.lang.Integer, java.lang.Object [ ]>
* @date: 2023/11/9 16:05
* @version: 1.0
**/
private Map<Integer, Object[]> getFieldsMap(Sheet sheet, int titleNum) {
//存储每列标题和每列的下标值
Map<String, Integer> cellMap = new HashMap<>();
//获取抬头行
Row titleRow = sheet.getRow(titleNum);
//获取标头行的总列数
int columnSize = sheet.getRow(titleNum).getPhysicalNumberOfCells();
// 遍历一行中每列值
for (int cellNum = 0; cellNum < columnSize; cellNum++) {
Cell cell = titleRow.getCell(cellNum);
if (cell != null) {
cellMap.put(cell.toString(), cellNum);
}
}
// 有数据时才处理 得到类的所有field.
List<Object[]> fields = this.getFields();
Map<Integer, Object[]> fieldsMap = new HashMap<>();
for (Object[] objects : fields) {
Excel attr = (Excel) objects[1];
Integer column = cellMap.get(attr.name());
if (column != null) {
fieldsMap.put(column, objects);
}
}
return fieldsMap;
}
/**
* @description: 获取图片集合
* @param: [file, sheet]
* @return: java.util.Map<java.lang.String, org.apache.poi.ss.usermodel.PictureData>
* @date: 2023/11/7 17:30
* @version: 1.0
**/
private Map<String, PictureData> readExcelPicture(Sheet sheet) {
// 声明当前页图片的集合
Map<String, PictureData> sheetImageMap;
// 获取图片
try {
//2003版本的excel,用.xls结尾
sheetImageMap = getPicturesHSS((HSSFSheet) sheet);
} catch (Exception ex) {
try {
//2007版本的excel,用.xlsx结尾
sheetImageMap = getPicturesXSS((XSSFSheet) sheet);
} catch (Exception e) {
log.error(ex.getMessage());
throw new ServiceException("解析图片异常!");
}
}
return sheetImageMap;
}
/**
* 获取图片和位置 (xls)
* @param sheet
* @return
* @throws IOException
*/
private Map<String, PictureData> getPicturesHSS(HSSFSheet sheet) {
Map<String, PictureData> map = new HashMap<String, PictureData>();
List<HSSFShape> list = sheet.getDrawingPatriarch().getChildren();
for (HSSFShape shape : list) {
if (shape instanceof HSSFPicture) {
HSSFPicture picture = (HSSFPicture) shape;
HSSFClientAnchor cAnchor = (HSSFClientAnchor) picture.getAnchor();
PictureData pdata = picture.getPictureData();
// 行号-列号
String key = cAnchor.getRow1() + "-" + cAnchor.getCol2();
map.put(key, pdata);
}
}
return map;
}
/**
* 获取图片和位置 (xlsx)
* @param sheet
* @return
* @throws IOException
*/
private Map<String, PictureData> getPicturesXSS(XSSFSheet sheet) {
Map<String, PictureData> sheetIndexPicMap = new HashMap<String, PictureData>();
for (POIXMLDocumentPart dr : sheet.getRelations()) {
if (dr instanceof XSSFDrawing) {
XSSFDrawing drawing = (XSSFDrawing) dr;
List<XSSFShape> shapes = drawing.getShapes();
for (XSSFShape shape : shapes) {
XSSFPicture pic = (XSSFPicture) shape;
//解决图片空指针报错问题 2021-12-27
XSSFClientAnchor anchor = (XSSFClientAnchor) shape.getAnchor();
//下面这个处理时间较长
//XSSFClientAnchor anchor = pic.getPreferredSize();
//CTMarker marker = anchor.getFrom();
// 行号-列号,该取用方式列准确率不高
//String key = marker.getRow() + "-" + marker.getCol();
//行号-列号
String key = anchor.getRow1() + "-" + (anchor.getCol2());
sheetIndexPicMap.put(key, pic.getPictureData());
}
}
}
return sheetIndexPicMap;
}
/**
* 读取excel
* @param file
* @return
*/
private Workbook readExcel(MultipartFile file) {
Workbook wb = null;
ZipSecureFile.setMinInflateRatio(0);
if (file == null) {
return null;
}
InputStream is;
try {
is = file.getInputStream();
//2003版本的excel,用.xls结尾
//得到工作簿
wb = new HSSFWorkbook(is);
} catch (Exception ex) {
log.error(ex.getMessage());
try {
//2007版本的excel,用.xlsx结尾
is = file.getInputStream();
//得到工作簿
wb = new XSSFWorkbook(is);
} catch (IOException e) {
log.error(ex.getMessage());
}
}
return wb;
}
/**
* 获取字段注解信息
*/
private List<Object[]> getFields() {
List<Object[]> fields = new ArrayList<Object[]>();
List<Field> tempFields = new ArrayList<>();
tempFields.addAll(Arrays.asList(clazz.getSuperclass().getDeclaredFields()));
tempFields.addAll(Arrays.asList(clazz.getDeclaredFields()));
for (Field field : tempFields) {
// 单注解
if (field.isAnnotationPresent(Excel.class)) {
Excel attr = field.getAnnotation(Excel.class);
if (attr != null && (attr.type() == Excel.Type.ALL || attr.type() == type)) {
field.setAccessible(true);
fields.add(new Object[]{field, attr});
}
}
// 多注解
if (field.isAnnotationPresent(Excels.class)) {
Excels attrs = field.getAnnotation(Excels.class);
Excel[] excels = (Excel[]) attrs.value();
for (Excel attr : excels) {
if (attr != null && (attr.type() == Excel.Type.ALL || attr.type() == type)) {
field.setAccessible(true);
fields.add(new Object[]{field, attr});
}
}
}
}
return fields;
}
/**
* 判断是否是空行
* @param row 判断的行
* @return
*/
private boolean isRowEmpty(Row row) {
if (row == null) {
return true;
}
for (int i = row.getFirstCellNum(); i < row.getLastCellNum(); i++) {
Cell cell = row.getCell(i);
if (cell != null && cell.getCellType() != CellType.BLANK) {
return false;
}
}
return true;
}
/**
* 格式化不同类型的日期对象
* @param dateFormat 日期格式
* @param val 被格式化的日期对象
* @return 格式化后的日期字符
*/
private String parseDateToStr(String dateFormat, Object val) {
if (val == null) {
return "";
}
String str;
if (val instanceof Date) {
str = new SimpleDateFormat(dateFormat).format((Date) val);
} else if (val instanceof LocalDateTime) {
str = new SimpleDateFormat(dateFormat).format(((LocalDateTime) val).atZone(ZoneId.systemDefault()).toInstant());
} else if (val instanceof LocalDate) {
LocalDate temporalAccessor = (LocalDate) val;
LocalDateTime localDateTime = LocalDateTime.of(temporalAccessor, LocalTime.of(0, 0, 0));
ZonedDateTime zdt = localDateTime.atZone(ZoneId.systemDefault());
str = new SimpleDateFormat(dateFormat).format(Date.from(zdt.toInstant()));
} else {
str = val.toString();
}
return str;
}
/**
* Excel注解集
*
*/
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Excels
{
Excel[] value();
}
package cn.goktech.module.utils;
import com.goktech.common.core.text.Convert;
import com.goktech.common.core.utils.DateUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.Validate;
import org.apache.poi.ss.usermodel.DateUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.lang.reflect.*;
import java.util.Date;
/**
* 反射工具类. 提供调用getter/setter方法, 访问私有变量, 调用私有方法, 获取泛型类型Class, 被AOP过的真实类等工具函数.
*
* @author ruoyi
*/
@SuppressWarnings("rawtypes")
public class ReflectUtils
{
private static final String SETTER_PREFIX = "set";
private static final String GETTER_PREFIX = "get";
private static final String CGLIB_CLASS_SEPARATOR = "$$";
private static Logger logger = LoggerFactory.getLogger(ReflectUtils.class);
/**
* 调用Getter方法.
* 支持多级,如:对象名.对象名.方法
*/
@SuppressWarnings("unchecked")
public static <E> E invokeGetter(Object obj, String propertyName)
{
Object object = obj;
for (String name : StringUtils.split(propertyName, "."))
{
String getterMethodName = GETTER_PREFIX + StringUtils.capitalize(name);
object = invokeMethod(object, getterMethodName, new Class[] {}, new Object[] {});
}
return (E) object;
}
/**
* 调用Setter方法, 仅匹配方法名。
* 支持多级,如:对象名.对象名.方法
*/
public static <E> void invokeSetter(Object obj, String propertyName, E value)
{
Object object = obj;
String[] names = StringUtils.split(propertyName, ".");
for (int i = 0; i < names.length; i++)
{
if (i < names.length - 1)
{
String getterMethodName = GETTER_PREFIX + StringUtils.capitalize(names[i]);
object = invokeMethod(object, getterMethodName, new Class[] {}, new Object[] {});
}
else
{
String setterMethodName = SETTER_PREFIX + StringUtils.capitalize(names[i]);
invokeMethodByName(object, setterMethodName, new Object[] { value });
}
}
}
/**
* 直接读取对象属性值, 无视private/protected修饰符, 不经过getter函数.
*/
@SuppressWarnings("unchecked")
public static <E> E getFieldValue(final Object obj, final String fieldName)
{
Field field = getAccessibleField(obj, fieldName);
if (field == null)
{
logger.debug("在 [" + obj.getClass() + "] 中,没有找到 [" + fieldName + "] 字段 ");
return null;
}
E result = null;
try
{
result = (E) field.get(obj);
}
catch (IllegalAccessException e)
{
logger.error("不可能抛出的异常{}", e.getMessage());
}
return result;
}
/**
* 直接设置对象属性值, 无视private/protected修饰符, 不经过setter函数.
*/
public static <E> void setFieldValue(final Object obj, final String fieldName, final E value)
{
Field field = getAccessibleField(obj, fieldName);
if (field == null)
{
// throw new IllegalArgumentException("在 [" + obj.getClass() + "] 中,没有找到 [" + fieldName + "] 字段 ");
logger.debug("在 [" + obj.getClass() + "] 中,没有找到 [" + fieldName + "] 字段 ");
return;
}
try
{
field.set(obj, value);
}
catch (IllegalAccessException e)
{
logger.error("不可能抛出的异常: {}", e.getMessage());
}
}
/**
* 直接调用对象方法, 无视private/protected修饰符.
* 用于一次性调用的情况,否则应使用getAccessibleMethod()函数获得Method后反复调用.
* 同时匹配方法名+参数类型,
*/
@SuppressWarnings("unchecked")
public static <E> E invokeMethod(final Object obj, final String methodName, final Class<?>[] parameterTypes,
final Object[] args)
{
if (obj == null || methodName == null)
{
return null;
}
Method method = getAccessibleMethod(obj, methodName, parameterTypes);
if (method == null)
{
logger.debug("在 [" + obj.getClass() + "] 中,没有找到 [" + methodName + "] 方法 ");
return null;
}
try
{
return (E) method.invoke(obj, args);
}
catch (Exception e)
{
String msg = "method: " + method + ", obj: " + obj + ", args: " + args + "";
throw convertReflectionExceptionToUnchecked(msg, e);
}
}
/**
* 直接调用对象方法, 无视private/protected修饰符,
* 用于一次性调用的情况,否则应使用getAccessibleMethodByName()函数获得Method后反复调用.
* 只匹配函数名,如果有多个同名函数调用第一个。
*/
@SuppressWarnings("unchecked")
public static <E> E invokeMethodByName(final Object obj, final String methodName, final Object[] args)
{
Method method = getAccessibleMethodByName(obj, methodName, args.length);
if (method == null)
{
// 如果为空不报错,直接返回空。
logger.debug("在 [" + obj.getClass() + "] 中,没有找到 [" + methodName + "] 方法 ");
return null;
}
try
{
// 类型转换(将参数数据类型转换为目标方法参数类型)
Class<?>[] cs = method.getParameterTypes();
for (int i = 0; i < cs.length; i++)
{
if (args[i] != null && !args[i].getClass().equals(cs[i]))
{
if (cs[i] == String.class)
{
args[i] = Convert.toStr(args[i]);
if (StringUtils.endsWith((String) args[i], ".0"))
{
args[i] = StringUtils.substringBefore((String) args[i], ".0");
}
}
else if (cs[i] == Integer.class)
{
args[i] = Convert.toInt(args[i]);
}
else if (cs[i] == Long.class)
{
args[i] = Convert.toLong(args[i]);
}
else if (cs[i] == Double.class)
{
args[i] = Convert.toDouble(args[i]);
}
else if (cs[i] == Float.class)
{
args[i] = Convert.toFloat(args[i]);
}
else if (cs[i] == Date.class)
{
if (args[i] instanceof String)
{
args[i] = DateUtils.parseDate(args[i]);
}
else
{
args[i] = DateUtil.getJavaDate((Double) args[i]);
}
}
else if (cs[i] == boolean.class || cs[i] == Boolean.class)
{
args[i] = Convert.toBool(args[i]);
}
}
}
return (E) method.invoke(obj, args);
}
catch (Exception e)
{
String msg = "method: " + method + ", obj: " + obj + ", args: " + args + "";
throw convertReflectionExceptionToUnchecked(msg, e);
}
}
/**
* 循环向上转型, 获取对象的DeclaredField, 并强制设置为可访问.
* 如向上转型到Object仍无法找到, 返回null.
*/
public static Field getAccessibleField(final Object obj, final String fieldName)
{
// 为空不报错。直接返回 null
if (obj == null)
{
return null;
}
Validate.notBlank(fieldName, "fieldName can't be blank");
for (Class<?> superClass = obj.getClass(); superClass != Object.class; superClass = superClass.getSuperclass())
{
try
{
Field field = superClass.getDeclaredField(fieldName);
makeAccessible(field);
return field;
}
catch (NoSuchFieldException e)
{
continue;
}
}
return null;
}
/**
* 循环向上转型, 获取对象的DeclaredMethod,并强制设置为可访问.
* 如向上转型到Object仍无法找到, 返回null.
* 匹配函数名+参数类型。
* 用于方法需要被多次调用的情况. 先使用本函数先取得Method,然后调用Method.invoke(Object obj, Object... args)
*/
public static Method getAccessibleMethod(final Object obj, final String methodName,
final Class<?>... parameterTypes)
{
// 为空不报错。直接返回 null
if (obj == null)
{
return null;
}
Validate.notBlank(methodName, "methodName can't be blank");
for (Class<?> searchType = obj.getClass(); searchType != Object.class; searchType = searchType.getSuperclass())
{
try
{
Method method = searchType.getDeclaredMethod(methodName, parameterTypes);
makeAccessible(method);
return method;
}
catch (NoSuchMethodException e)
{
continue;
}
}
return null;
}
/**
* 循环向上转型, 获取对象的DeclaredMethod,并强制设置为可访问.
* 如向上转型到Object仍无法找到, 返回null.
* 只匹配函数名。
* 用于方法需要被多次调用的情况. 先使用本函数先取得Method,然后调用Method.invoke(Object obj, Object... args)
*/
public static Method getAccessibleMethodByName(final Object obj, final String methodName, int argsNum)
{
// 为空不报错。直接返回 null
if (obj == null)
{
return null;
}
Validate.notBlank(methodName, "methodName can't be blank");
for (Class<?> searchType = obj.getClass(); searchType != Object.class; searchType = searchType.getSuperclass())
{
Method[] methods = searchType.getDeclaredMethods();
for (Method method : methods)
{
if (method.getName().equals(methodName) && method.getParameterTypes().length == argsNum)
{
makeAccessible(method);
return method;
}
}
}
return null;
}
/**
* 改变private/protected的方法为public,尽量不调用实际改动的语句,避免JDK的SecurityManager抱怨。
*/
public static void makeAccessible(Method method)
{
if ((!Modifier.isPublic(method.getModifiers()) || !Modifier.isPublic(method.getDeclaringClass().getModifiers()))
&& !method.isAccessible())
{
method.setAccessible(true);
}
}
/**
* 改变private/protected的成员变量为public,尽量不调用实际改动的语句,避免JDK的SecurityManager抱怨。
*/
public static void makeAccessible(Field field)
{
if ((!Modifier.isPublic(field.getModifiers()) || !Modifier.isPublic(field.getDeclaringClass().getModifiers())
|| Modifier.isFinal(field.getModifiers())) && !field.isAccessible())
{
field.setAccessible(true);
}
}
/**
* 通过反射, 获得Class定义中声明的泛型参数的类型, 注意泛型必须定义在父类处
* 如无法找到, 返回Object.class.
*/
@SuppressWarnings("unchecked")
public static <T> Class<T> getClassGenricType(final Class clazz)
{
return getClassGenricType(clazz, 0);
}
/**
* 通过反射, 获得Class定义中声明的父类的泛型参数的类型.
* 如无法找到, 返回Object.class.
*/
public static Class getClassGenricType(final Class clazz, final int index)
{
Type genType = clazz.getGenericSuperclass();
if (!(genType instanceof ParameterizedType))
{
logger.debug(clazz.getSimpleName() + "'s superclass not ParameterizedType");
return Object.class;
}
Type[] params = ((ParameterizedType) genType).getActualTypeArguments();
if (index >= params.length || index < 0)
{
logger.debug("Index: " + index + ", Size of " + clazz.getSimpleName() + "'s Parameterized Type: "
+ params.length);
return Object.class;
}
if (!(params[index] instanceof Class))
{
logger.debug(clazz.getSimpleName() + " not set the actual class on superclass generic parameter");
return Object.class;
}
return (Class) params[index];
}
public static Class<?> getUserClass(Object instance)
{
if (instance == null)
{
throw new RuntimeException("Instance must not be null");
}
Class clazz = instance.getClass();
if (clazz != null && clazz.getName().contains(CGLIB_CLASS_SEPARATOR))
{
Class<?> superClass = clazz.getSuperclass();
if (superClass != null && !Object.class.equals(superClass))
{
return superClass;
}
}
return clazz;
}
/**
* 将反射时的checked exception转换为unchecked exception.
*/
public static RuntimeException convertReflectionExceptionToUnchecked(String msg, Exception e)
{
if (e instanceof IllegalAccessException || e instanceof IllegalArgumentException
|| e instanceof NoSuchMethodException)
{
return new IllegalArgumentException(msg, e);
}
else if (e instanceof InvocationTargetException)
{
return new RuntimeException(msg, ((InvocationTargetException) e).getTargetException());
}
return new RuntimeException(msg, e);
}
}
这里就结束了,通过上面的步骤就可以精确获取到每一个图片数据,当然要注意你表格中的图片位置放好了没有,有可能位置不对也会影响读取失败的。
有疑问可留言,看到了就回文章来源:https://www.toymoban.com/news/detail-790505.html
有帮到你,给个三连哦!文章来源地址https://www.toymoban.com/news/detail-790505.html
到了这里,关于Java 通过POI快速导入带图片的excel并且图片不会丢失的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!