Commit 0187344798c2d4cb2a1b0d89a3e73dcc6bbb0a07
1 parent
a9c6ba12
Excel注解支持Image图片导出
Showing
4 changed files
with
211 additions
and
9 deletions
ruoyi-common/src/main/java/com/ruoyi/common/annotation/Excel.java
| @@ -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) |
ruoyi-common/src/main/java/com/ruoyi/common/utils/file/FileTypeUtils.java
| @@ -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 | } |
| 48 | \ No newline at end of file | 77 | \ No newline at end of file |
ruoyi-common/src/main/java/com/ruoyi/common/utils/file/ImageUtils.java
0 → 100644
| 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 | +} |
ruoyi-common/src/main/java/com/ruoyi/common/utils/poi/ExcelUtil.java
| @@ -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 | /** |