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
ruoyi-common/src/main/java/com/ruoyi/common/utils/file/FileTypeUtils.java
| ... | ... | @@ -44,4 +44,33 @@ public class FileTypeUtils |
| 44 | 44 | } |
| 45 | 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 | 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 | 22 | import org.apache.poi.ss.usermodel.Cell; |
| 23 | 23 | import org.apache.poi.ss.usermodel.CellStyle; |
| 24 | 24 | import org.apache.poi.ss.usermodel.CellType; |
| 25 | +import org.apache.poi.ss.usermodel.ClientAnchor; | |
| 25 | 26 | import org.apache.poi.ss.usermodel.DataValidation; |
| 26 | 27 | import org.apache.poi.ss.usermodel.DataValidationConstraint; |
| 27 | 28 | import org.apache.poi.ss.usermodel.DataValidationHelper; |
| 28 | 29 | import org.apache.poi.ss.usermodel.DateUtil; |
| 30 | +import org.apache.poi.ss.usermodel.Drawing; | |
| 29 | 31 | import org.apache.poi.ss.usermodel.FillPatternType; |
| 30 | 32 | import org.apache.poi.ss.usermodel.Font; |
| 31 | 33 | import org.apache.poi.ss.usermodel.HorizontalAlignment; |
| ... | ... | @@ -37,6 +39,7 @@ import org.apache.poi.ss.usermodel.Workbook; |
| 37 | 39 | import org.apache.poi.ss.usermodel.WorkbookFactory; |
| 38 | 40 | import org.apache.poi.ss.util.CellRangeAddressList; |
| 39 | 41 | import org.apache.poi.xssf.streaming.SXSSFWorkbook; |
| 42 | +import org.apache.poi.xssf.usermodel.XSSFClientAnchor; | |
| 40 | 43 | import org.apache.poi.xssf.usermodel.XSSFDataValidation; |
| 41 | 44 | import org.slf4j.Logger; |
| 42 | 45 | import org.slf4j.LoggerFactory; |
| ... | ... | @@ -51,6 +54,8 @@ import com.ruoyi.common.exception.CustomException; |
| 51 | 54 | import com.ruoyi.common.utils.DateUtils; |
| 52 | 55 | import com.ruoyi.common.utils.DictUtils; |
| 53 | 56 | import com.ruoyi.common.utils.StringUtils; |
| 57 | +import com.ruoyi.common.utils.file.FileTypeUtils; | |
| 58 | +import com.ruoyi.common.utils.file.ImageUtils; | |
| 54 | 59 | import com.ruoyi.common.utils.reflect.ReflectUtils; |
| 55 | 60 | |
| 56 | 61 | /** |
| ... | ... | @@ -103,15 +108,20 @@ public class ExcelUtil<T> |
| 103 | 108 | private List<Object[]> fields; |
| 104 | 109 | |
| 105 | 110 | /** |
| 111 | + * 最大高度 | |
| 112 | + */ | |
| 113 | + private short maxHeight; | |
| 114 | + | |
| 115 | + /** | |
| 106 | 116 | * 统计列表 |
| 107 | 117 | */ |
| 108 | 118 | private Map<Integer, Double> statistics = new HashMap<Integer, Double>(); |
| 109 | - | |
| 119 | + | |
| 110 | 120 | /** |
| 111 | 121 | * 数字格式 |
| 112 | 122 | */ |
| 113 | 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 | 249 | } |
| 240 | 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 | 263 | else if ((Integer.TYPE == fieldType || Integer.class == fieldType) && StringUtils.isNumeric(Convert.toStr(val))) |
| ... | ... | @@ -521,6 +539,47 @@ public class ExcelUtil<T> |
| 521 | 539 | { |
| 522 | 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 | 595 | { |
| 537 | 596 | // 设置列宽 |
| 538 | 597 | sheet.setColumnWidth(column, (int) ((attr.width() + 0.72) * 256)); |
| 539 | - row.setHeight((short) (attr.height() * 20)); | |
| 540 | 598 | } |
| 541 | 599 | // 如果设置了提示信息则鼠标放上去提示. |
| 542 | 600 | if (StringUtils.isNotEmpty(attr.prompt())) |
| ... | ... | @@ -561,7 +619,7 @@ public class ExcelUtil<T> |
| 561 | 619 | try |
| 562 | 620 | { |
| 563 | 621 | // 设置行高 |
| 564 | - row.setHeight((short) (attr.height() * 20)); | |
| 622 | + row.setHeight(maxHeight); | |
| 565 | 623 | // 根据Excel中设置情况决定是否导出,有些情况需要保持为空,希望用户填写这一列. |
| 566 | 624 | if (attr.isExport()) |
| 567 | 625 | { |
| ... | ... | @@ -737,7 +795,7 @@ public class ExcelUtil<T> |
| 737 | 795 | } |
| 738 | 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 | 821 | { |
| 764 | 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 | 858 | cell = row.createCell(0); |
| 801 | 859 | cell.setCellStyle(styles.get("total")); |
| 802 | 860 | cell.setCellValue("合计"); |
| 803 | - | |
| 861 | + | |
| 804 | 862 | for (Integer key : keys) |
| 805 | 863 | { |
| 806 | 864 | cell = row.createCell(key); |
| ... | ... | @@ -916,6 +974,21 @@ public class ExcelUtil<T> |
| 916 | 974 | } |
| 917 | 975 | } |
| 918 | 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 | /** | ... | ... |