作者 RuoYi

Excel注解支持Image图片导出

@@ -149,7 +149,7 @@ public @interface Excel @@ -149,7 +149,7 @@ public @interface Excel
149 149
150 public enum ColumnType 150 public enum ColumnType
151 { 151 {
152 - NUMERIC(0), STRING(1); 152 + NUMERIC(0), STRING(1), IMAGE(2);
153 private final int value; 153 private final int value;
154 154
155 ColumnType(int value) 155 ColumnType(int value)
@@ -44,4 +44,33 @@ public class FileTypeUtils @@ -44,4 +44,33 @@ public class FileTypeUtils
44 } 44 }
45 return fileName.substring(separatorIndex + 1).toLowerCase(); 45 return fileName.substring(separatorIndex + 1).toLowerCase();
46 } 46 }
  47 +
  48 + /**
  49 + * 获取文件类型
  50 + *
  51 + * @param photoByte 文件字节码
  52 + * @return 后缀(不含".")
  53 + */
  54 + public static String getFileExtendName(byte[] photoByte)
  55 + {
  56 + String strFileExtendName = "JPG";
  57 + if ((photoByte[0] == 71) && (photoByte[1] == 73) && (photoByte[2] == 70) && (photoByte[3] == 56)
  58 + && ((photoByte[4] == 55) || (photoByte[4] == 57)) && (photoByte[5] == 97))
  59 + {
  60 + strFileExtendName = "GIF";
  61 + }
  62 + else if ((photoByte[6] == 74) && (photoByte[7] == 70) && (photoByte[8] == 73) && (photoByte[9] == 70))
  63 + {
  64 + strFileExtendName = "JPG";
  65 + }
  66 + else if ((photoByte[0] == 66) && (photoByte[1] == 77))
  67 + {
  68 + strFileExtendName = "BMP";
  69 + }
  70 + else if ((photoByte[1] == 80) && (photoByte[2] == 78) && (photoByte[3] == 71))
  71 + {
  72 + strFileExtendName = "PNG";
  73 + }
  74 + return strFileExtendName;
  75 + }
47 } 76 }
  1 +package com.ruoyi.common.utils.file;
  2 +
  3 +import java.io.ByteArrayInputStream;
  4 +import java.io.ByteArrayOutputStream;
  5 +import java.io.FileInputStream;
  6 +import java.io.InputStream;
  7 +import java.net.URL;
  8 +import java.net.URLConnection;
  9 +import java.util.Arrays;
  10 +import org.apache.poi.util.IOUtils;
  11 +import org.slf4j.Logger;
  12 +import org.slf4j.LoggerFactory;
  13 +import com.ruoyi.common.config.RuoYiConfig;
  14 +import com.ruoyi.common.constant.Constants;
  15 +import com.ruoyi.common.utils.StringUtils;
  16 +
  17 +/**
  18 + * 图片处理工具类
  19 + *
  20 + * @author ruoyi
  21 + */
  22 +public class ImageUtils
  23 +{
  24 + private static final Logger log = LoggerFactory.getLogger(ImageUtils.class);
  25 +
  26 + public static byte[] getImage(String imagePath)
  27 + {
  28 + InputStream is = getFile(imagePath);
  29 + try
  30 + {
  31 + return IOUtils.toByteArray(is);
  32 + }
  33 + catch (Exception e)
  34 + {
  35 + log.error("图片加载异常 {}", e);
  36 + return null;
  37 + }
  38 + finally
  39 + {
  40 + IOUtils.closeQuietly(is);
  41 + }
  42 + }
  43 +
  44 + public static InputStream getFile(String imagePath)
  45 + {
  46 + try
  47 + {
  48 + byte[] result = readFile(imagePath);
  49 + result = Arrays.copyOf(result, result.length);
  50 + return new ByteArrayInputStream(result);
  51 + }
  52 + catch (Exception e)
  53 + {
  54 + log.error("获取图片异常 {}", e);
  55 + }
  56 + return null;
  57 + }
  58 +
  59 + /**
  60 + * 读取文件为字节数据
  61 + *
  62 + * @param key 地址
  63 + * @return 字节数据
  64 + */
  65 + public static byte[] readFile(String url)
  66 + {
  67 + InputStream in = null;
  68 + ByteArrayOutputStream baos = null;
  69 + try
  70 + {
  71 + if (url.startsWith("http"))
  72 + {
  73 + // 网络地址
  74 + URL urlObj = new URL(url);
  75 + URLConnection urlConnection = urlObj.openConnection();
  76 + urlConnection.setConnectTimeout(30 * 1000);
  77 + urlConnection.setReadTimeout(60 * 1000);
  78 + urlConnection.setDoInput(true);
  79 + in = urlConnection.getInputStream();
  80 + }
  81 + else
  82 + {
  83 + // 本机地址
  84 + String localPath = RuoYiConfig.getProfile();
  85 + String downloadPath = localPath + StringUtils.substringAfter(url, Constants.RESOURCE_PREFIX);
  86 + in = new FileInputStream(downloadPath);
  87 + }
  88 + return IOUtils.toByteArray(in);
  89 + }
  90 + catch (Exception e)
  91 + {
  92 + log.error("获取文件路径异常 {}", e);
  93 + return null;
  94 + }
  95 + finally
  96 + {
  97 + IOUtils.closeQuietly(baos);
  98 + }
  99 + }
  100 +}
@@ -22,10 +22,12 @@ import org.apache.poi.ss.usermodel.BorderStyle; @@ -22,10 +22,12 @@ import org.apache.poi.ss.usermodel.BorderStyle;
22 import org.apache.poi.ss.usermodel.Cell; 22 import org.apache.poi.ss.usermodel.Cell;
23 import org.apache.poi.ss.usermodel.CellStyle; 23 import org.apache.poi.ss.usermodel.CellStyle;
24 import org.apache.poi.ss.usermodel.CellType; 24 import org.apache.poi.ss.usermodel.CellType;
  25 +import org.apache.poi.ss.usermodel.ClientAnchor;
25 import org.apache.poi.ss.usermodel.DataValidation; 26 import org.apache.poi.ss.usermodel.DataValidation;
26 import org.apache.poi.ss.usermodel.DataValidationConstraint; 27 import org.apache.poi.ss.usermodel.DataValidationConstraint;
27 import org.apache.poi.ss.usermodel.DataValidationHelper; 28 import org.apache.poi.ss.usermodel.DataValidationHelper;
28 import org.apache.poi.ss.usermodel.DateUtil; 29 import org.apache.poi.ss.usermodel.DateUtil;
  30 +import org.apache.poi.ss.usermodel.Drawing;
29 import org.apache.poi.ss.usermodel.FillPatternType; 31 import org.apache.poi.ss.usermodel.FillPatternType;
30 import org.apache.poi.ss.usermodel.Font; 32 import org.apache.poi.ss.usermodel.Font;
31 import org.apache.poi.ss.usermodel.HorizontalAlignment; 33 import org.apache.poi.ss.usermodel.HorizontalAlignment;
@@ -37,6 +39,7 @@ import org.apache.poi.ss.usermodel.Workbook; @@ -37,6 +39,7 @@ import org.apache.poi.ss.usermodel.Workbook;
37 import org.apache.poi.ss.usermodel.WorkbookFactory; 39 import org.apache.poi.ss.usermodel.WorkbookFactory;
38 import org.apache.poi.ss.util.CellRangeAddressList; 40 import org.apache.poi.ss.util.CellRangeAddressList;
39 import org.apache.poi.xssf.streaming.SXSSFWorkbook; 41 import org.apache.poi.xssf.streaming.SXSSFWorkbook;
  42 +import org.apache.poi.xssf.usermodel.XSSFClientAnchor;
40 import org.apache.poi.xssf.usermodel.XSSFDataValidation; 43 import org.apache.poi.xssf.usermodel.XSSFDataValidation;
41 import org.slf4j.Logger; 44 import org.slf4j.Logger;
42 import org.slf4j.LoggerFactory; 45 import org.slf4j.LoggerFactory;
@@ -51,6 +54,8 @@ import com.ruoyi.common.exception.CustomException; @@ -51,6 +54,8 @@ import com.ruoyi.common.exception.CustomException;
51 import com.ruoyi.common.utils.DateUtils; 54 import com.ruoyi.common.utils.DateUtils;
52 import com.ruoyi.common.utils.DictUtils; 55 import com.ruoyi.common.utils.DictUtils;
53 import com.ruoyi.common.utils.StringUtils; 56 import com.ruoyi.common.utils.StringUtils;
  57 +import com.ruoyi.common.utils.file.FileTypeUtils;
  58 +import com.ruoyi.common.utils.file.ImageUtils;
54 import com.ruoyi.common.utils.reflect.ReflectUtils; 59 import com.ruoyi.common.utils.reflect.ReflectUtils;
55 60
56 /** 61 /**
@@ -103,15 +108,20 @@ public class ExcelUtil<T> @@ -103,15 +108,20 @@ public class ExcelUtil<T>
103 private List<Object[]> fields; 108 private List<Object[]> fields;
104 109
105 /** 110 /**
  111 + * 最大高度
  112 + */
  113 + private short maxHeight;
  114 +
  115 + /**
106 * 统计列表 116 * 统计列表
107 */ 117 */
108 private Map<Integer, Double> statistics = new HashMap<Integer, Double>(); 118 private Map<Integer, Double> statistics = new HashMap<Integer, Double>();
109 - 119 +
110 /** 120 /**
111 * 数字格式 121 * 数字格式
112 */ 122 */
113 private static final DecimalFormat DOUBLE_FORMAT = new DecimalFormat("######0.00"); 123 private static final DecimalFormat DOUBLE_FORMAT = new DecimalFormat("######0.00");
114 - 124 +
115 /** 125 /**
116 * 实体对象 126 * 实体对象
117 */ 127 */
@@ -239,7 +249,15 @@ public class ExcelUtil<T> @@ -239,7 +249,15 @@ public class ExcelUtil<T>
239 } 249 }
240 else 250 else
241 { 251 {
242 - val = Convert.toStr(val); 252 + String dateFormat = field.getAnnotation(Excel.class).dateFormat();
  253 + if (StringUtils.isNotEmpty(dateFormat))
  254 + {
  255 + val = DateUtils.parseDateToStr(dateFormat, (Date) val);
  256 + }
  257 + else
  258 + {
  259 + val = Convert.toStr(val);
  260 + }
243 } 261 }
244 } 262 }
245 else if ((Integer.TYPE == fieldType || Integer.class == fieldType) && StringUtils.isNumeric(Convert.toStr(val))) 263 else if ((Integer.TYPE == fieldType || Integer.class == fieldType) && StringUtils.isNumeric(Convert.toStr(val)))
@@ -521,6 +539,47 @@ public class ExcelUtil<T> @@ -521,6 +539,47 @@ public class ExcelUtil<T>
521 { 539 {
522 cell.setCellValue(StringUtils.contains(Convert.toStr(value), ".") ? Convert.toDouble(value) : Convert.toInt(value)); 540 cell.setCellValue(StringUtils.contains(Convert.toStr(value), ".") ? Convert.toDouble(value) : Convert.toInt(value));
523 } 541 }
  542 + else if (ColumnType.IMAGE == attr.cellType())
  543 + {
  544 + ClientAnchor anchor = new XSSFClientAnchor(0, 0, 0, 0, (short) cell.getColumnIndex(), cell.getRow().getRowNum(), (short) (cell.getColumnIndex() + 1),
  545 + cell.getRow().getRowNum() + 1);
  546 + String imagePath = Convert.toStr(value);
  547 + if (StringUtils.isNotEmpty(imagePath))
  548 + {
  549 + byte[] data = ImageUtils.getImage(imagePath);
  550 + getDrawingPatriarch(cell.getSheet()).createPicture(anchor,
  551 + cell.getSheet().getWorkbook().addPicture(data, getImageType(data)));
  552 + }
  553 + }
  554 + }
  555 +
  556 + /**
  557 + * 获取画布
  558 + */
  559 + public static Drawing<?> getDrawingPatriarch(Sheet sheet)
  560 + {
  561 + if (sheet.getDrawingPatriarch() == null)
  562 + {
  563 + sheet.createDrawingPatriarch();
  564 + }
  565 + return sheet.getDrawingPatriarch();
  566 + }
  567 +
  568 + /**
  569 + * 获取图片类型,设置图片插入类型
  570 + */
  571 + public int getImageType(byte[] value)
  572 + {
  573 + String type = FileTypeUtils.getFileExtendName(value);
  574 + if ("JPG".equalsIgnoreCase(type))
  575 + {
  576 + return Workbook.PICTURE_TYPE_JPEG;
  577 + }
  578 + else if ("PNG".equalsIgnoreCase(type))
  579 + {
  580 + return Workbook.PICTURE_TYPE_PNG;
  581 + }
  582 + return Workbook.PICTURE_TYPE_JPEG;
524 } 583 }
525 584
526 /** 585 /**
@@ -536,7 +595,6 @@ public class ExcelUtil<T> @@ -536,7 +595,6 @@ public class ExcelUtil<T>
536 { 595 {
537 // 设置列宽 596 // 设置列宽
538 sheet.setColumnWidth(column, (int) ((attr.width() + 0.72) * 256)); 597 sheet.setColumnWidth(column, (int) ((attr.width() + 0.72) * 256));
539 - row.setHeight((short) (attr.height() * 20));  
540 } 598 }
541 // 如果设置了提示信息则鼠标放上去提示. 599 // 如果设置了提示信息则鼠标放上去提示.
542 if (StringUtils.isNotEmpty(attr.prompt())) 600 if (StringUtils.isNotEmpty(attr.prompt()))
@@ -561,7 +619,7 @@ public class ExcelUtil<T> @@ -561,7 +619,7 @@ public class ExcelUtil<T>
561 try 619 try
562 { 620 {
563 // 设置行高 621 // 设置行高
564 - row.setHeight((short) (attr.height() * 20)); 622 + row.setHeight(maxHeight);
565 // 根据Excel中设置情况决定是否导出,有些情况需要保持为空,希望用户填写这一列. 623 // 根据Excel中设置情况决定是否导出,有些情况需要保持为空,希望用户填写这一列.
566 if (attr.isExport()) 624 if (attr.isExport())
567 { 625 {
@@ -737,7 +795,7 @@ public class ExcelUtil<T> @@ -737,7 +795,7 @@ public class ExcelUtil<T>
737 } 795 }
738 return StringUtils.stripEnd(propertyString.toString(), separator); 796 return StringUtils.stripEnd(propertyString.toString(), separator);
739 } 797 }
740 - 798 +
741 /** 799 /**
742 * 解析字典值 800 * 解析字典值
743 * 801 *
@@ -763,7 +821,7 @@ public class ExcelUtil<T> @@ -763,7 +821,7 @@ public class ExcelUtil<T>
763 { 821 {
764 return DictUtils.getDictValue(dictType, dictLabel, separator); 822 return DictUtils.getDictValue(dictType, dictLabel, separator);
765 } 823 }
766 - 824 +
767 /** 825 /**
768 * 合计统计信息 826 * 合计统计信息
769 */ 827 */
@@ -800,7 +858,7 @@ public class ExcelUtil<T> @@ -800,7 +858,7 @@ public class ExcelUtil<T>
800 cell = row.createCell(0); 858 cell = row.createCell(0);
801 cell.setCellStyle(styles.get("total")); 859 cell.setCellStyle(styles.get("total"));
802 cell.setCellValue("合计"); 860 cell.setCellValue("合计");
803 - 861 +
804 for (Integer key : keys) 862 for (Integer key : keys)
805 { 863 {
806 cell = row.createCell(key); 864 cell = row.createCell(key);
@@ -916,6 +974,21 @@ public class ExcelUtil<T> @@ -916,6 +974,21 @@ public class ExcelUtil<T>
916 } 974 }
917 } 975 }
918 this.fields = this.fields.stream().sorted(Comparator.comparing(objects -> ((Excel) objects[1]).sort())).collect(Collectors.toList()); 976 this.fields = this.fields.stream().sorted(Comparator.comparing(objects -> ((Excel) objects[1]).sort())).collect(Collectors.toList());
  977 + this.maxHeight = getRowHeight();
  978 + }
  979 +
  980 + /**
  981 + * 根据注解获取最大行高
  982 + */
  983 + public short getRowHeight()
  984 + {
  985 + double maxHeight = 0;
  986 + for (Object[] os : this.fields)
  987 + {
  988 + Excel excel = (Excel) os[1];
  989 + maxHeight = maxHeight > excel.height() ? maxHeight : excel.height();
  990 + }
  991 + return (short) (maxHeight * 20);
919 } 992 }
920 993
921 /** 994 /**