作者 RuoYi

Excel注解支持Image图片导入

@@ -91,6 +91,14 @@ public class RuoYiConfig @@ -91,6 +91,14 @@ public class RuoYiConfig
91 } 91 }
92 92
93 /** 93 /**
  94 + * 获取导入上传路径
  95 + */
  96 + public static String getImportPath()
  97 + {
  98 + return getProfile() + "/import";
  99 + }
  100 +
  101 + /**
94 * 获取头像上传路径 102 * 获取头像上传路径
95 */ 103 */
96 public static String getAvatarPath() 104 public static String getAvatarPath()
@@ -127,7 +127,7 @@ public class FileUploadUtils @@ -127,7 +127,7 @@ public class FileUploadUtils
127 return fileName; 127 return fileName;
128 } 128 }
129 129
130 - private static final File getAbsoluteFile(String uploadDir, String fileName) throws IOException 130 + public static final File getAbsoluteFile(String uploadDir, String fileName) throws IOException
131 { 131 {
132 File desc = new File(uploadDir + File.separator + fileName); 132 File desc = new File(uploadDir + File.separator + fileName);
133 133
@@ -141,7 +141,7 @@ public class FileUploadUtils @@ -141,7 +141,7 @@ public class FileUploadUtils
141 return desc; 141 return desc;
142 } 142 }
143 143
144 - private static final String getPathFileName(String uploadDir, String fileName) throws IOException 144 + public static final String getPathFileName(String uploadDir, String fileName) throws IOException
145 { 145 {
146 int dirLastIndex = RuoYiConfig.getProfile().length() + 1; 146 int dirLastIndex = RuoYiConfig.getProfile().length() + 1;
147 String currentDir = StringUtils.substring(uploadDir, dirLastIndex); 147 String currentDir = StringUtils.substring(uploadDir, dirLastIndex);
@@ -3,6 +3,7 @@ package com.ruoyi.common.utils.file; @@ -3,6 +3,7 @@ package com.ruoyi.common.utils.file;
3 import java.io.File; 3 import java.io.File;
4 import java.io.FileInputStream; 4 import java.io.FileInputStream;
5 import java.io.FileNotFoundException; 5 import java.io.FileNotFoundException;
  6 +import java.io.FileOutputStream;
6 import java.io.IOException; 7 import java.io.IOException;
7 import java.io.OutputStream; 8 import java.io.OutputStream;
8 import java.io.UnsupportedEncodingException; 9 import java.io.UnsupportedEncodingException;
@@ -10,8 +11,12 @@ import java.net.URLEncoder; @@ -10,8 +11,12 @@ import java.net.URLEncoder;
10 import java.nio.charset.StandardCharsets; 11 import java.nio.charset.StandardCharsets;
11 import javax.servlet.http.HttpServletRequest; 12 import javax.servlet.http.HttpServletRequest;
12 import javax.servlet.http.HttpServletResponse; 13 import javax.servlet.http.HttpServletResponse;
  14 +import org.apache.commons.io.IOUtils;
13 import org.apache.commons.lang3.ArrayUtils; 15 import org.apache.commons.lang3.ArrayUtils;
  16 +import com.ruoyi.common.config.RuoYiConfig;
  17 +import com.ruoyi.common.utils.DateUtils;
14 import com.ruoyi.common.utils.StringUtils; 18 import com.ruoyi.common.utils.StringUtils;
  19 +import com.ruoyi.common.utils.uuid.IdUtils;
15 20
16 /** 21 /**
17 * 文件处理工具类 22 * 文件处理工具类
@@ -53,29 +58,48 @@ public class FileUtils @@ -53,29 +58,48 @@ public class FileUtils
53 } 58 }
54 finally 59 finally
55 { 60 {
56 - if (os != null)  
57 - {  
58 - try  
59 - {  
60 - os.close(); 61 + IOUtils.close(os);
  62 + IOUtils.close(fis);
61 } 63 }
62 - catch (IOException e1)  
63 - {  
64 - e1.printStackTrace();  
65 } 64 }
  65 +
  66 + /**
  67 + * 写数据到文件中
  68 + *
  69 + * @param data 数据
  70 + * @return 目标文件
  71 + * @throws IOException IO异常
  72 + */
  73 + public static String writeImportBytes(byte[] data) throws IOException
  74 + {
  75 + return writeBytes(data, RuoYiConfig.getImportPath());
66 } 76 }
67 - if (fis != null) 77 +
  78 + /**
  79 + * 写数据到文件中
  80 + *
  81 + * @param data 数据
  82 + * @param uploadDir 目标文件
  83 + * @return 目标文件
  84 + * @throws IOException IO异常
  85 + */
  86 + public static String writeBytes(byte[] data, String uploadDir) throws IOException
68 { 87 {
  88 + FileOutputStream fos = null;
  89 + String pathName = "";
69 try 90 try
70 { 91 {
71 - fis.close(); 92 + String extension = getFileExtendName(data);
  93 + pathName = DateUtils.datePath() + "/" + IdUtils.fastUUID() + "." + extension;
  94 + File file = FileUploadUtils.getAbsoluteFile(uploadDir, pathName);
  95 + fos = new FileOutputStream(file);
  96 + fos.write(data);
72 } 97 }
73 - catch (IOException e1) 98 + finally
74 { 99 {
75 - e1.printStackTrace();  
76 - }  
77 - } 100 + IOUtils.close(fos);
78 } 101 }
  102 + return FileUploadUtils.getPathFileName(uploadDir, pathName);
79 } 103 }
80 104
81 /** 105 /**
@@ -200,4 +224,33 @@ public class FileUtils @@ -200,4 +224,33 @@ public class FileUtils
200 String encode = URLEncoder.encode(s, StandardCharsets.UTF_8.toString()); 224 String encode = URLEncoder.encode(s, StandardCharsets.UTF_8.toString());
201 return encode.replaceAll("\\+", "%20"); 225 return encode.replaceAll("\\+", "%20");
202 } 226 }
  227 +
  228 + /**
  229 + * 获取图像后缀
  230 + *
  231 + * @param photoByte 图像数据
  232 + * @return 后缀名
  233 + */
  234 + public static String getFileExtendName(byte[] photoByte)
  235 + {
  236 + String strFileExtendName = "jpg";
  237 + if ((photoByte[0] == 71) && (photoByte[1] == 73) && (photoByte[2] == 70) && (photoByte[3] == 56)
  238 + && ((photoByte[4] == 55) || (photoByte[4] == 57)) && (photoByte[5] == 97))
  239 + {
  240 + strFileExtendName = "gif";
  241 + }
  242 + else if ((photoByte[6] == 74) && (photoByte[7] == 70) && (photoByte[8] == 73) && (photoByte[9] == 70))
  243 + {
  244 + strFileExtendName = "jpg";
  245 + }
  246 + else if ((photoByte[0] == 66) && (photoByte[1] == 77))
  247 + {
  248 + strFileExtendName = "bmp";
  249 + }
  250 + else if ((photoByte[1] == 80) && (photoByte[2] == 78) && (photoByte[3] == 71))
  251 + {
  252 + strFileExtendName = "png";
  253 + }
  254 + return strFileExtendName;
  255 + }
203 } 256 }
@@ -18,6 +18,8 @@ import java.util.Map; @@ -18,6 +18,8 @@ import java.util.Map;
18 import java.util.Set; 18 import java.util.Set;
19 import java.util.UUID; 19 import java.util.UUID;
20 import java.util.stream.Collectors; 20 import java.util.stream.Collectors;
  21 +import javax.servlet.http.HttpServletResponse;
  22 +import org.apache.poi.ooxml.POIXMLDocumentPart;
21 import org.apache.poi.ss.usermodel.BorderStyle; 23 import org.apache.poi.ss.usermodel.BorderStyle;
22 import org.apache.poi.ss.usermodel.Cell; 24 import org.apache.poi.ss.usermodel.Cell;
23 import org.apache.poi.ss.usermodel.CellStyle; 25 import org.apache.poi.ss.usermodel.CellStyle;
@@ -32,15 +34,23 @@ import org.apache.poi.ss.usermodel.FillPatternType; @@ -32,15 +34,23 @@ import org.apache.poi.ss.usermodel.FillPatternType;
32 import org.apache.poi.ss.usermodel.Font; 34 import org.apache.poi.ss.usermodel.Font;
33 import org.apache.poi.ss.usermodel.HorizontalAlignment; 35 import org.apache.poi.ss.usermodel.HorizontalAlignment;
34 import org.apache.poi.ss.usermodel.IndexedColors; 36 import org.apache.poi.ss.usermodel.IndexedColors;
  37 +import org.apache.poi.ss.usermodel.PictureData;
35 import org.apache.poi.ss.usermodel.Row; 38 import org.apache.poi.ss.usermodel.Row;
36 import org.apache.poi.ss.usermodel.Sheet; 39 import org.apache.poi.ss.usermodel.Sheet;
37 import org.apache.poi.ss.usermodel.VerticalAlignment; 40 import org.apache.poi.ss.usermodel.VerticalAlignment;
38 import org.apache.poi.ss.usermodel.Workbook; 41 import org.apache.poi.ss.usermodel.Workbook;
39 import org.apache.poi.ss.usermodel.WorkbookFactory; 42 import org.apache.poi.ss.usermodel.WorkbookFactory;
40 import org.apache.poi.ss.util.CellRangeAddressList; 43 import org.apache.poi.ss.util.CellRangeAddressList;
  44 +import org.apache.poi.util.IOUtils;
41 import org.apache.poi.xssf.streaming.SXSSFWorkbook; 45 import org.apache.poi.xssf.streaming.SXSSFWorkbook;
42 import org.apache.poi.xssf.usermodel.XSSFClientAnchor; 46 import org.apache.poi.xssf.usermodel.XSSFClientAnchor;
43 import org.apache.poi.xssf.usermodel.XSSFDataValidation; 47 import org.apache.poi.xssf.usermodel.XSSFDataValidation;
  48 +import org.apache.poi.xssf.usermodel.XSSFDrawing;
  49 +import org.apache.poi.xssf.usermodel.XSSFPicture;
  50 +import org.apache.poi.xssf.usermodel.XSSFShape;
  51 +import org.apache.poi.xssf.usermodel.XSSFSheet;
  52 +import org.apache.poi.xssf.usermodel.XSSFWorkbook;
  53 +import org.openxmlformats.schemas.drawingml.x2006.spreadsheetDrawing.CTMarker;
44 import org.slf4j.Logger; 54 import org.slf4j.Logger;
45 import org.slf4j.LoggerFactory; 55 import org.slf4j.LoggerFactory;
46 import com.ruoyi.common.annotation.Excel; 56 import com.ruoyi.common.annotation.Excel;
@@ -55,6 +65,7 @@ import com.ruoyi.common.utils.DateUtils; @@ -55,6 +65,7 @@ import com.ruoyi.common.utils.DateUtils;
55 import com.ruoyi.common.utils.DictUtils; 65 import com.ruoyi.common.utils.DictUtils;
56 import com.ruoyi.common.utils.StringUtils; 66 import com.ruoyi.common.utils.StringUtils;
57 import com.ruoyi.common.utils.file.FileTypeUtils; 67 import com.ruoyi.common.utils.file.FileTypeUtils;
  68 +import com.ruoyi.common.utils.file.FileUtils;
58 import com.ruoyi.common.utils.file.ImageUtils; 69 import com.ruoyi.common.utils.file.ImageUtils;
59 import com.ruoyi.common.utils.reflect.ReflectUtils; 70 import com.ruoyi.common.utils.reflect.ReflectUtils;
60 71
@@ -168,24 +179,15 @@ public class ExcelUtil<T> @@ -168,24 +179,15 @@ public class ExcelUtil<T>
168 this.type = Type.IMPORT; 179 this.type = Type.IMPORT;
169 this.wb = WorkbookFactory.create(is); 180 this.wb = WorkbookFactory.create(is);
170 List<T> list = new ArrayList<T>(); 181 List<T> list = new ArrayList<T>();
171 - Sheet sheet = null;  
172 - if (StringUtils.isNotEmpty(sheetName))  
173 - {  
174 - // 如果指定sheet名,则取指定sheet中的内容.  
175 - sheet = wb.getSheet(sheetName);  
176 - }  
177 - else  
178 - {  
179 - // 如果传入的sheet名不存在则默认指向第1个sheet.  
180 - sheet = wb.getSheetAt(0);  
181 - }  
182 - 182 + // 如果指定sheet名,则取指定sheet中的内容 否则默认指向第1个sheet
  183 + Sheet sheet = StringUtils.isNotEmpty(sheetName) ? wb.getSheet(sheetName) : wb.getSheetAt(0);
183 if (sheet == null) 184 if (sheet == null)
184 { 185 {
185 throw new IOException("文件sheet不存在"); 186 throw new IOException("文件sheet不存在");
186 } 187 }
187 -  
188 - int rows = sheet.getPhysicalNumberOfRows(); 188 + Map<String, PictureData> pictures = getSheetPictrues((XSSFSheet) sheet, (XSSFWorkbook) wb);
  189 + // 获取最后一个非空行的行下标,比如总行数为n,则返回的为n-1
  190 + int rows = sheet.getLastRowNum();
189 191
190 if (rows > 0) 192 if (rows > 0)
191 { 193 {
@@ -225,11 +227,12 @@ public class ExcelUtil<T> @@ -225,11 +227,12 @@ public class ExcelUtil<T>
225 } 227 }
226 } 228 }
227 } 229 }
228 - for (int i = 1; i < rows; i++) 230 + for (int i = 1; i <= rows; i++)
229 { 231 {
230 // 从第2行开始取数据,默认第一行是表头. 232 // 从第2行开始取数据,默认第一行是表头.
231 Row row = sheet.getRow(i); 233 Row row = sheet.getRow(i);
232 - if(row == null) 234 + // 判断当前行是否是空行
  235 + if (isRowEmpty(row))
233 { 236 {
234 continue; 237 continue;
235 } 238 }
@@ -315,6 +318,20 @@ public class ExcelUtil<T> @@ -315,6 +318,20 @@ public class ExcelUtil<T>
315 { 318 {
316 val = reverseDictByExp(Convert.toStr(val), attr.dictType(), attr.separator()); 319 val = reverseDictByExp(Convert.toStr(val), attr.dictType(), attr.separator());
317 } 320 }
  321 + else if (ColumnType.IMAGE == attr.cellType())
  322 + {
  323 + if (StringUtils.isNull(pictures))
  324 + {
  325 + val = "";
  326 + }
  327 + PictureData image = pictures.get(row.getRowNum() + "_" + entry.getKey());
  328 + if (image == null)
  329 + {
  330 + val = "";
  331 + }
  332 + byte[] data = image.getData();
  333 + val = FileUtils.writeImportBytes(data);
  334 + }
318 ReflectUtils.invokeSetter(entity, propertyName, val); 335 ReflectUtils.invokeSetter(entity, propertyName, val);
319 } 336 }
320 } 337 }
@@ -340,6 +357,23 @@ public class ExcelUtil<T> @@ -340,6 +357,23 @@ public class ExcelUtil<T>
340 /** 357 /**
341 * 对list数据源将其里面的数据导入到excel表单 358 * 对list数据源将其里面的数据导入到excel表单
342 * 359 *
  360 + * @param response 返回数据
  361 + * @param list 导出数据集合
  362 + * @param sheetName 工作表的名称
  363 + * @return 结果
  364 + * @throws IOException
  365 + */
  366 + public void exportExcel(HttpServletResponse response, List<T> list, String sheetName) throws IOException
  367 + {
  368 + response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
  369 + response.setCharacterEncoding("utf-8");
  370 + this.init(list, sheetName, Type.EXPORT);
  371 + exportExcel(response.getOutputStream());
  372 + }
  373 +
  374 + /**
  375 + * 对list数据源将其里面的数据导入到excel表单
  376 + *
343 * @param sheetName 工作表的名称 377 * @param sheetName 工作表的名称
344 * @return 结果 378 * @return 结果
345 */ 379 */
@@ -352,34 +386,51 @@ public class ExcelUtil<T> @@ -352,34 +386,51 @@ public class ExcelUtil<T>
352 /** 386 /**
353 * 对list数据源将其里面的数据导入到excel表单 387 * 对list数据源将其里面的数据导入到excel表单
354 * 388 *
  389 + * @param sheetName 工作表的名称
355 * @return 结果 390 * @return 结果
356 */ 391 */
357 - public AjaxResult exportExcel() 392 + public void importTemplateExcel(HttpServletResponse response, String sheetName) throws IOException
358 { 393 {
359 - OutputStream out = null;  
360 - try 394 + response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
  395 + response.setCharacterEncoding("utf-8");
  396 + this.init(null, sheetName, Type.IMPORT);
  397 + exportExcel(response.getOutputStream());
  398 + }
  399 +
  400 + /**
  401 + * 对list数据源将其里面的数据导入到excel表单
  402 + *
  403 + * @return 结果
  404 + */
  405 + public void exportExcel(OutputStream out)
361 { 406 {
362 - // 取出一共有多少个sheet.  
363 - double sheetNo = Math.ceil(list.size() / sheetSize);  
364 - for (int index = 0; index <= sheetNo; index++) 407 + try
365 { 408 {
366 - createSheet(sheetNo, index);  
367 -  
368 - // 产生一行  
369 - Row row = sheet.createRow(0);  
370 - int column = 0;  
371 - // 写入各个字段的列头名称  
372 - for (Object[] os : fields) 409 + writeSheet();
  410 + wb.write(out);
  411 + }
  412 + catch (Exception e)
373 { 413 {
374 - Excel excel = (Excel) os[1];  
375 - this.createCell(excel, row, column++); 414 + log.error("导出Excel异常{}", e.getMessage());
376 } 415 }
377 - if (Type.EXPORT.equals(type)) 416 + finally
378 { 417 {
379 - fillExcelData(index, row);  
380 - addStatisticsRow(); 418 + IOUtils.closeQuietly(wb);
  419 + IOUtils.closeQuietly(out);
381 } 420 }
382 } 421 }
  422 +
  423 + /**
  424 + * 对list数据源将其里面的数据导入到excel表单
  425 + *
  426 + * @return 结果
  427 + */
  428 + public AjaxResult exportExcel()
  429 + {
  430 + OutputStream out = null;
  431 + try
  432 + {
  433 + writeSheet();
383 String filename = encodingFilename(sheetName); 434 String filename = encodingFilename(sheetName);
384 out = new FileOutputStream(getAbsoluteFile(filename)); 435 out = new FileOutputStream(getAbsoluteFile(filename));
385 wb.write(out); 436 wb.write(out);
@@ -392,27 +443,35 @@ public class ExcelUtil<T> @@ -392,27 +443,35 @@ public class ExcelUtil<T>
392 } 443 }
393 finally 444 finally
394 { 445 {
395 - if (wb != null)  
396 - {  
397 - try  
398 - {  
399 - wb.close();  
400 - }  
401 - catch (IOException e1)  
402 - {  
403 - e1.printStackTrace(); 446 + IOUtils.closeQuietly(wb);
  447 + IOUtils.closeQuietly(out);
404 } 448 }
405 } 449 }
406 - if (out != null) 450 +
  451 + /**
  452 + * 创建写入数据到Sheet
  453 + */
  454 + public void writeSheet()
407 { 455 {
408 - try 456 + // 取出一共有多少个sheet.
  457 + double sheetNo = Math.ceil(list.size() / sheetSize);
  458 + for (int index = 0; index <= sheetNo; index++)
409 { 459 {
410 - out.close();  
411 - }  
412 - catch (IOException e1) 460 + createSheet(sheetNo, index);
  461 +
  462 + // 产生一行
  463 + Row row = sheet.createRow(0);
  464 + int column = 0;
  465 + // 写入各个字段的列头名称
  466 + for (Object[] os : fields)
413 { 467 {
414 - e1.printStackTrace(); 468 + Excel excel = (Excel) os[1];
  469 + this.createCell(excel, row, column++);
415 } 470 }
  471 + if (Type.EXPORT.equals(type))
  472 + {
  473 + fillExcelData(index, row);
  474 + addStatisticsRow();
416 } 475 }
417 } 476 }
418 } 477 }
@@ -548,8 +607,7 @@ public class ExcelUtil<T> @@ -548,8 +607,7 @@ public class ExcelUtil<T>
548 } 607 }
549 else if (ColumnType.IMAGE == attr.cellType()) 608 else if (ColumnType.IMAGE == attr.cellType())
550 { 609 {
551 - ClientAnchor anchor = new XSSFClientAnchor(0, 0, 0, 0, (short) cell.getColumnIndex(), cell.getRow().getRowNum(), (short) (cell.getColumnIndex() + 1),  
552 - cell.getRow().getRowNum() + 1); 610 + ClientAnchor anchor = new XSSFClientAnchor(0, 0, 0, 0, (short) cell.getColumnIndex(), cell.getRow().getRowNum(), (short) (cell.getColumnIndex() + 1), cell.getRow().getRowNum() + 1);
553 String imagePath = Convert.toStr(value); 611 String imagePath = Convert.toStr(value);
554 if (StringUtils.isNotEmpty(imagePath)) 612 if (StringUtils.isNotEmpty(imagePath))
555 { 613 {
@@ -859,10 +917,9 @@ public class ExcelUtil<T> @@ -859,10 +917,9 @@ public class ExcelUtil<T>
859 { 917 {
860 if (statistics.size() > 0) 918 if (statistics.size() > 0)
861 { 919 {
862 - Cell cell = null;  
863 Row row = sheet.createRow(sheet.getLastRowNum() + 1); 920 Row row = sheet.createRow(sheet.getLastRowNum() + 1);
864 Set<Integer> keys = statistics.keySet(); 921 Set<Integer> keys = statistics.keySet();
865 - cell = row.createCell(0); 922 + Cell cell = row.createCell(0);
866 cell.setCellStyle(styles.get("total")); 923 cell.setCellStyle(styles.get("total"));
867 cell.setCellValue("合计"); 924 cell.setCellValue("合计");
868 925
@@ -1097,4 +1154,59 @@ public class ExcelUtil<T> @@ -1097,4 +1154,59 @@ public class ExcelUtil<T>
1097 } 1154 }
1098 return val; 1155 return val;
1099 } 1156 }
  1157 +
  1158 + /**
  1159 + * 判断是否是空行
  1160 + *
  1161 + * @param row 判断的行
  1162 + * @return
  1163 + */
  1164 + private boolean isRowEmpty(Row row)
  1165 + {
  1166 + if (row == null)
  1167 + {
  1168 + return true;
  1169 + }
  1170 + for (int i = row.getFirstCellNum(); i < row.getLastCellNum(); i++)
  1171 + {
  1172 + Cell cell = row.getCell(i);
  1173 + if (cell != null && cell.getCellType() != CellType.BLANK)
  1174 + {
  1175 + return false;
  1176 + }
  1177 + }
  1178 + return true;
  1179 + }
  1180 +
  1181 + /**
  1182 + * 获取Excel图片
  1183 + *
  1184 + * @param sheet 当前sheet对象
  1185 + * @param workbook 工作簿对象
  1186 + * @return Map key:图片单元格索引(1_1)String,value:图片流PictureData
  1187 + */
  1188 + public static Map<String, PictureData> getSheetPictrues(XSSFSheet sheet, XSSFWorkbook workbook)
  1189 + {
  1190 + Map<String, PictureData> sheetIndexPicMap = new HashMap<String, PictureData>();
  1191 + for (POIXMLDocumentPart dr : sheet.getRelations())
  1192 + {
  1193 + if (dr instanceof XSSFDrawing)
  1194 + {
  1195 + XSSFDrawing drawing = (XSSFDrawing) dr;
  1196 + List<XSSFShape> shapes = drawing.getShapes();
  1197 + for (XSSFShape shape : shapes)
  1198 + {
  1199 + if (shape instanceof XSSFPicture)
  1200 + {
  1201 + XSSFPicture pic = (XSSFPicture) shape;
  1202 + XSSFClientAnchor anchor = pic.getPreferredSize();
  1203 + CTMarker ctMarker = anchor.getFrom();
  1204 + String picIndex = ctMarker.getRow() + "_" + ctMarker.getCol();
  1205 + sheetIndexPicMap.put(picIndex, pic.getPictureData());
  1206 + }
  1207 + }
  1208 + }
  1209 + }
  1210 + return sheetIndexPicMap;
  1211 + }
1100 } 1212 }