基於SSM框架的Excel工具類
阿新 • • 發佈:2019-01-30
前言
- 本次提供Excel工具類匯入和匯出的功能,是本人在借鑑網上的部分程式碼的基礎上搭出一個公用化的工具。如有不妥之處,麻煩指明,本人不勝感激。
Excel匯出
實現思路
- 通過Apache提供POI包讀取Excel資訊,首先讀取指定欄位名錶頭,其次從指定行數開始讀取資料,然和對應的欄位名對映到Map,最後返回一個List實現步驟
- 匯入Maven依賴
<dependency >
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
<version>3.17</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId> poi-ooxml</artifactId>
<version>3.17</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml-schemas</artifactId>
<version>3.17</version>
</dependency >
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-excelant</artifactId>
<version>3.17</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-examples</artifactId>
<version>3.17</version>
</dependency>
- 定義上傳Excel格式
- 接收MultiPartFile型別的資料
- 呼叫ExcelUtil 工具
ExcelUtils utils = new ExcelUtils();
//欄位名讀取開始索引
utils.setFieldReadIndex(4);
//資料讀取開始索引
utils.setDataReadIndex(6);
List<Map> maps = utils.getExcelInfo(file.getOriginalFilename(), file);
- 接收到List
for(Map map :maps){
//這裡Bean是要用來接收的自定義實體類
Bean obj = MapToBeanUtils.getModel(map,Bean.class);
//處理相關業務邏輯
}
Excel匯出
實現思路
- 藉助自定義註解在匯出實體類的欄位標註該欄位對應的表頭名稱,再通過ExcelUtils工具解析實體類資料和其欄位註解來生成Excel。
實現步驟
- 匯入Maven依賴
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
<version>3.17</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>3.17</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml-schemas</artifactId>
<version>3.17</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-excelant</artifactId>
<version>3.17</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-examples</artifactId>
<version>3.17</version>
</dependency>
- 自定義註解
@Target(ElementType.FIELD)
@Documented
@Retention(RetentionPolicy.RUNTIME)
public @interface ExcelColumn {
String title();
}
- 匯出實體類引用註解形式
@ExcelColumn(title="uuid")
private String uuid;
- 引用工具類
//這裡Bean是要用來匯出的自定義實體類
ExcelUtils.export(Bean.class, list, DateUtil.format(new Date(),DateUtil.INT_STRING_FORMAT), response);
原始碼
ExcelUtils.java
package com.demo.util;
import com.andacx.core.log.LogFactory;
import com.andacx.tool.Asserts;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.hssf.util.HSSFColor;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.ss.util.CellUtil;
import org.apache.poi.xssf.streaming.SXSSFSheet;
import org.apache.poi.xssf.streaming.SXSSFWorkbook;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.slf4j.Logger;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.commons.CommonsMultipartFile;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.lang.reflect.Field;
import java.util.*;
/**
* <p>@description:【Excel寫入和匯出工具】</p>
* <p>@author:【Boomer】</p>
* <p>@date:【2018/1/11 11:18】</p>
**/
public class ExcelUtils {
private static final Logger LOGGER = LogFactory.get(ExcelUtils.class);
/*** 總行數 **/
private int totalRows = 0;
/*** 總條數 **/
private int totalCells = 0;
/*** 資料讀取開始索引 **/
private int dataReadIndex = 3;
/*** 欄位名讀取開始索引 **/
private int fieldReadIndex = 2;
public ExcelUtils() {
}
public int getTotalRows() {
return totalRows;
}
public int getTotalCells() {
return totalCells;
}
public int getDataReadIndex() {
return dataReadIndex;
}
public void setDataReadIndex(int dataReadIndex) {
this.dataReadIndex = dataReadIndex;
}
public int getFieldReadIndex() {
return fieldReadIndex;
}
public void setFieldReadIndex(int fieldReadIndex) {
this.fieldReadIndex = fieldReadIndex;
}
/**
* <p>Description:【驗證EXCEL檔案】</p>
* <p>Author:【Boomer】</p>
* <p>Date:【2018/1/11 14:07】</p>
* @param filePath
* @return
**/
public boolean validateExcel(String filePath) {
Asserts.notNull(filePath, "檔名不是excel格式");
Asserts.isTrue((isExcel2003(filePath) || isExcel2007(filePath)), "檔名不是excel格式");
return true;
}
/**
* <p>Description:【是否是2003的excel,返回true是2003】</p>
* <p>Author:【Boomer】</p>
* <p>Date:【2018/1/11 14:07】</p>
* @param filePath
* @return
**/
public static boolean isExcel2003(String filePath) {
return filePath.matches("^.+.(xls)$");
}
/**
* <p>Description:【是否是2007的excel,返回true是2007】</p>
* <p>Author:【Boomer】</p>
* <p>Date:【2018/1/11 14:07】</p>
* @param filePath
* @return
**/
public static boolean isExcel2007(String filePath) {
return filePath.matches("^.+.(xlsx)$");
}
/**
* <p>Description:【讀EXCEL檔案,獲取客戶資訊集合】</p>
* <p>Author:【Boomer】</p>
* <p>Date:【2018/1/11 14:07】</p>
* @param fileName
* @param Mfile
* @return
**/
public List<Map> getExcelInfo(String fileName, MultipartFile Mfile) {
//把spring檔案上傳的MultipartFile轉換成CommonsMultipartFile型別
CommonsMultipartFile cf = (CommonsMultipartFile) Mfile;
//獲取本地儲存路徑
File fileFolder = new File(Sysutils.getUploadFilePath());
if (!fileFolder.exists()) {
fileFolder.mkdirs();
}
//新建一個檔案
File uploadFile = new File(Sysutils.getUploadFilePath() + System.currentTimeMillis() + ".xlsx");
//將上傳的檔案寫入新建的檔案中
try {
cf.getFileItem().write(uploadFile);
} catch (Exception e) {
e.printStackTrace();
}
//初始化資訊的集合
List<Map> list = new ArrayList<>();
//初始化輸入流
InputStream is = null;
try {
//驗證檔名是否合格
if (!validateExcel(fileName)) {
return null;
}
//根據新建的檔案例項化輸入流
is = new FileInputStream(uploadFile);
//根據excel裡面的內容讀取客戶資訊
list = getExcelInfo(is, isExcel2003(fileName));
is.close();
} catch (Exception e) {
e.printStackTrace();
} finally {
if (is != null) {
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return list;
}
/**
* <p>Description:【根據excel裡面的內容讀取客戶資訊】</p>
* <p>Author:【Boomer】</p>
* <p>Date:【2018/1/11 14:07】</p>
* @param is 輸入流
* @param isExcel2003 excel是2003還是2007版本
* @return
* @throws IOException
**/
public List<Map> getExcelInfo(InputStream is, boolean isExcel2003) {
List<Map> list;
try {
/** 根據版本選擇建立Workbook的方式 */
Workbook wb;
//當excel是2003時
if (isExcel2003) {
wb = new HSSFWorkbook(is);
} else {//當excel是2007時
wb = new XSSFWorkbook(is);
}
//讀取Excel裡面客戶的資訊
list = readExcelValue(wb);
} catch (IOException e) {
throw new RuntimeException(e.getMessage());
} catch (NumberFormatException e) {
throw new RuntimeException("讀取錯誤,請確認Excel是否包含錯誤字元");
}
return list;
}
/**
* <p>Description:【讀取Excel裡面客戶的資訊】</p>
* <p>Author:【Boomer】</p>
* <p>Date:【2018/1/11 14:07】</p>
* @param wb
* @return
**/
private List<Map> readExcelValue(Workbook wb) {
//得到第一個shell
Sheet sheet = wb.getSheetAt(0);
//得到Excel的行數
this.totalRows = sheet.getPhysicalNumberOfRows();
//得到Excel的列數(前提是有行數)
if (totalRows >= this.dataReadIndex && sheet.getRow(0) != null) {
this.totalCells = sheet.getRow(0).getPhysicalNumberOfCells();
}
List<Map> list = new ArrayList<>();
Map tempMap;
//獲取列資料對應的欄位
Row title = sheet.getRow(this.fieldReadIndex);
List<String> header = new ArrayList<>();
//迴圈Excel的列
for (int c = 0; c < this.totalCells; c++) {
Cell cell = title.getCell(c);
header.add(cell.getStringCellValue());
}
//迴圈Excel行數,從第二行開始。標題不入庫
for (int r = this.dataReadIndex; r < totalRows; r++) {
Row row = sheet.getRow(r);
if (row == null) {
continue;
}
tempMap = new HashMap(this.totalCells);
//迴圈Excel的列
for (int c = 0; c < this.totalCells; c++) {
Cell cell = row.getCell(c);
if (null != cell) {
cell.setCellType(CellType.STRING);
tempMap.put(header.get(c), cell.getStringCellValue());
}
}
//新增客戶
list.add(tempMap);
}
return list;
}
/**
* <p>Description:【匯出到file】</p>
* <p>Author:【Boomer】</p>
* <p>Date:【2018/1/11 14:07】</p>
* @param clz
* @param data
* @param filePath
* @param <T>
* @throws IOException
**/
public static <T> void export(Class<T> clz, List<? extends T> data, String filePath) throws IOException {
FileOutputStream out = new FileOutputStream(filePath);
getBook(clz, data).write(out);
out.close();
}
/**
* <p>Description:【匯出到httpServletResponse】</p>
* <p>Author:【Boomer】</p>
* <p>Date:【2018/1/11 14:07】</p>
* @param clz
* @param data
* @param fileName
* @param response
* @param <T>
**/
public static <T> void export(Class<T> clz, List<? extends T> data, String fileName, HttpServletResponse response) {
try {
response.setContentType("application/vnd.ms-excel");
response.setHeader("Content-disposition", "attachment;filename=\"" + new String(fileName.getBytes("gb2312"), "ISO8859-1") + ".xlsx" + "\"");
getBook(clz, data).write(response.getOutputStream());
} catch (Exception e) {
throw new RuntimeException("系統異常");
} finally {
try {
response.getOutputStream().close();
} catch (IOException e) {
LOGGER.error("流關閉異常:" + e.getMessage());
}
}
}
/**
* <p>Description:【生成Excel】</p>
* <p>Author:【Boomer】</p>
* <p>Date:【2018/1/11 14:07】</p>
* @param clz
* @param data
* @return
**/
private static <T> SXSSFWorkbook getBook(Class<T> clz, List<? extends T> data) {
if (null == clz || null == data || data.size() <= 0) {
throw new RuntimeException("引數異常");
}
XSSFWorkbook workbook = new XSSFWorkbook();
SXSSFWorkbook sxssfWorkbook = new SXSSFWorkbook(workbook, 100);
//第一個工作本
SXSSFSheet sheet = sxssfWorkbook.createSheet();
sheet.trackAllColumnsForAutoSizing();
//表頭
Row header = sheet.createRow(0);
//讀取類欄位、註解資訊
Field[] fields = clz.getDeclaredFields();
//寫入表頭
int invalidFieldNum = 0;
for (Field field : fields) {
ExcelColumn excelColumn = field.getAnnotation(ExcelColumn.class);
if (excelColumn == null) {
continue;
}
Cell headerCell = header.createCell(invalidFieldNum++);
headerCell.setCellStyle(headerCellStyle(sxssfWorkbook));
headerCell.setCellValue(excelColumn.title());
}
//寫入資料
for (int i = 0; i < data.size(); i++) {
//第0行為表頭
Row row = sheet.createRow(i + 1);
//迴圈讀取欄位
int cellIndex = 0;
for (int j = 0; j < fields.length; j++) {
Field field = fields[j];
field.setAccessible(true);
Object cellValue;
ExcelColumn excelColumn = field.getAnnotation(ExcelColumn.class);
if (null == excelColumn) {
continue;
}
Object o;
try {
o = field.get(data.get(i));
cellValue = o;
} catch (Exception e) {
throw new RuntimeException("系統異常");
}
CellUtil.createCell(row, cellIndex++, String.valueOf(cellValue));
}
}
//自適應寬度
for (int i = 0; i < invalidFieldNum; i++) {
sheet.autoSizeColumn(i);
}
return sxssfWorkbook;
}
/**
* <p>Description:【設定Excel表頭樣式】</p>
* <p>Author:【Boomer】</p>
* <p>Date:【2018/1/11 14:07】</p>
* @param wb
* @return
**/
public static CellStyle headerCellStyle(Workbook wb) {
CellStyle style = wb.createCellStyle();
Font font = wb.createFont();
font.setFontName("宋體");
//設定字型大小
font.setFontHeightInPoints((short) 12);
//加粗
font.setBold(true);
// 設定背景色
style.setFillForegroundColor(HSSFColor.HSSFColorPredefined.SKY_BLUE.getIndex());
style.setFillPattern(FillPatternType.SOLID_FOREGROUND);
//讓單元格居中
style.setAlignment(HorizontalAlignment.CENTER_SELECTION);
// 左右居中
style.setAlignment(HorizontalAlignment.CENTER);
// 上下居中
style.setVerticalAlignment(VerticalAlignment.CENTER);
//設定自動換行
style.setWrapText(true);
style.setFont(font);
return style;
}
}
map轉實體工具類
package com.andacx.lbs.util;
import com.andacx.tool.StringUtil;
import org.apache.commons.collections.MapUtils;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
/**
* Created by Boomer on 2017/5/19.
* @author Boomer
*/
public class MapToBeanUtils {
/**
* 獲取類型別的所有Field包括父類中的Field
*
* @param clazz 類型別
* @return 返回類型別的所有Field包括父類中的Field
*/
public static Field[] getAllFields(Class clazz) {
Map<String, Field> map = new HashMap<String, Field>();
for (Field field : clazz.getDeclaredFields()) {
Class fieldType = field.getType();
String fieldName = field.getName();
if(isBaseType(fieldType)){
try {
map.put(fieldName, field);
} catch (Exception e) {
e.printStackTrace();
}
}
}
while (clazz.getSuperclass() != null) {
clazz = clazz.getSuperclass();
if (clazz == Object.class) {
break;
}
for (Field field : clazz.getDeclaredFields()) {
if (!map.containsKey(field.getName())) {
map.put(field.getName(), field);
}
}
}
return map.values().toArray(new Field[map.size()]);
}
/**
* <p>Description:【判斷是否是基本型別】</p>
* <p>Author:【Boomer】</p>
* <p>Date:【2018/1/8 16:13】</p>
* @param fieldType
* @return
**/
public static Boolean isBaseType(Class fieldType){
if (fieldType == String.class) {
return true;
} else if (fieldType == double.class || fieldType == Double.class) {
return true;
} else if (fieldType == int.class || fieldType == Integer.class) {
return true;
} else if (fieldType == Boolean.class || fieldType == boolean.class) {
return true;
} else if (fieldType == Date.class) {
return true;
}
return false;
}
/**
* <p>Description:【將首字母轉換為大寫】</p>
* <p>Author:【Boomer】</p>
* <p>Date:【2018/1/5 15:54】</p>
*
* @param a
* @return
**/
public static String firstCharToUpper(String a) {
return (new StringBuilder()).insert(0, a.substring(0, 1).toUpperCase()).append(a.substring(1)).toString();
}
/**
* <p>Description:【將map隱射到實體類】</p>
* <p>Author:【Boomer】</p>
* <p>Date:【2018/1/5 16:27】</p>
*
* @param map
* @param model
* @return
**/
public static <T> T getModel(Map map, Class<T> model) {
Field[] fields = getAllFields(model);
String value;
Method method;
T obj;
try {
obj = model.newInstance();
} catch (Exception e) {
throw new RuntimeException(e);
}
for (Field field : fields) {
int modifiers = field.getModifiers();
if (Modifier.isStatic(modifiers) || Modifier.isFinal(modifiers)) {
continue;
}
//根據傳入的獲取str來判斷用哪個字串獲取引數
value = MapUtils.getString(map, field.getName());
if (StringUtil.isEmpty(value)) {
if (field.getType() == String.class) {
try {
method = model.getMethod("get" + firstCharToUpper(field.getName()));
Object invokeValue = method.invoke(obj);
if (invokeValue != null) {
value = invokeValue.toString();
}
} catch (Exception e) {
if (field.getType() == Boolean.class || field.getType() == boolean.class) {
try {
String fieldName = field.getName();
method = model.getMethod("get" + firstCharToUpper(fieldName.startsWith("is") ? fieldName.replace("is", "") : fieldName));
Object invokeValue = method.invoke(obj);
if (invokeValue != null) {
value = invokeValue.toString();
}
} catch (Exception e1) {
System.out.println(e1.getMessage());
}
} else {
System.out.println(e.getMessage());
}
}
}
}
if (StringUtil.isNotEmpty(value)) {
invokeMethod(model, obj, value, field.getName(), field.getType());
}
}
return obj;
}
/**
* <p>Description:【呼叫方法】</p>
* <p>Author:【Boomer】</p>
* <p>Date:【2018/1/5 16:26】</p>
*
* @param modelClass
* @param object
* @param value
* @param fieldName
* @param fieldType
* @return
**/
private static <T> void invokeMethod(Class<T> modelClass, T object, String value, String fieldName, Class fieldType) {
Method method = null;
try {
method = modelClass.getMethod("set" + firstCharToUpper(fieldName), fieldType);
} catch (NoSuchMethodException e) {
if (fieldType == Boolean.class || fieldType == boolean.class) {
try {
method = modelClass.getMethod("set" + firstCharToUpper(fieldName.startsWith("is") ? fieldName.replace("is", "") : fieldName), fieldType);
} catch (NoSuchMethodException e1) {
}
} else {
}
}
try {
if (null != method) {
if (fieldType == String.class) {
method.invoke(object, value);
} else if (fieldType == double.class || fieldType == Double.class) {
try {
method.invoke(object, Double.parseDouble(value));
} catch (NumberFormatException e) {
e.printStackTrace();
}
} else if (fieldType == int.class || fieldType == Integer.class) {
try {
method.invoke(object, Integer.parseInt(value));
} catch (NumberFormatException e) {
e.printStackTrace();
}
} else if (fieldType == Boolean.class || fieldType == boolean.class) {
try {
method.invoke(object, Boolean.parseBoolean(value));
} catch (Exception e) {
e.printStackTrace();
}
} else if (fieldType == Date.class) {
try {
SimpleDateFormat sdf = (value.length() == 10 ? new SimpleDateFormat("yyyy-MM-dd") : new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
method.invoke(object, sdf.parse(value));
} catch (Exception e) {
e.printStackTrace();
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
- 20180122
這個工具也可以用以下方法實現
public static <T> List<T> mapToBean(List<Map> maps, Class<T> obj) throws IllegalAccessException, InstantiationException {
List<T> list = new ArrayList<>();
for (Map<String, Object> map : maps) {
T module = obj.newInstance();
BeanWrapper wapper = new BeanWrapperImpl(module);
for (Map.Entry entry : map.entrySet()) {
if (wapper.isWritableProperty(entry.getKey().toString())) {
wapper.setPropertyValue(entry.getKey().toString(), entry.getValue().toString());
}
}
list.add(module);
}
return list;
}
後語
- 原始碼中可能摻雜一些個人工具類原始碼,各位可自行更改,不影響功能實現。如果問題請留言。