假設現在有這樣一個需求:
1) 批量導入使用者,需要校驗使用者的資訊
2) 如果有錯誤的資料支援導出,有錯誤資訊的單元格用特殊顔色标出,并将錯誤資訊設定在單元格批注裡
針對以上需求,筆者對EasyPoi進行了封裝,下面将依次介紹
1 導入
excel導入資料如下圖
EasyPoi支援hibernate-validator注解式校驗,如下圖
如果要擷取校驗沒通過的錯誤資訊及行号需要實作IExcelDataModel和IExcelModel接口。這些都是基本校驗,在實際開發過程中可能會遇到需要寫代碼來校驗,比如校驗使用者名是否重複,EasyPoi給出了校驗處理器,可以實作自定義校驗。
@Component
public class UserImportVerifyHandler implements IExcelVerifyHandler {
@Resource
private UserMapper userMapper;
@Override
public ExcelVerifyHandlerResult verifyHandler(UserExcel excelBo) {
StringJoiner joiner = new StringJoiner(",");
UserInfo userInfo = userMapper.findByName(excelBo.getUserName());
if (userInfo != null) {
joiner.add("使用者名不允許重複#0");
}
return new ExcelVerifyHandlerResult(false, joiner.toString());
}
}
以上校驗需要在導入參數裡開啟
public void importUser(MultipartFile file) {
//導入參數
ImportParams params = new ImportParams();
//開啟校驗
params.setNeedVerify(true);
//校驗處理器
params.setVerifyHandler(verifyHandler);
//調用模闆方法導入excel
this.importExcel(file, UserExcel.class, params);
}
在上面的校驗資訊裡面加了字尾,比如使用者名不允許重複#0,這裡面的#0是用來辨別錯誤資訊所在的列,0就表示列,#是友善将0截取出來,在導出的時候會用到。讀者可以采用其它的方式,比如将錯誤資訊與列做個映射。一整行的錯誤資訊都會存到errorMsg這個字段裡,并用“,”分隔。
下面看下封裝的抽象類
public abstract class AbstractImportService {
@Resource
private TransactionalHelper transactionalHelper;
public void importExcel(MultipartFile file, Class> pojoClass, ImportParams params) {
ExcelImportResult result = null;
try {
//調用EasyPoi的導入接口
result = ExcelImportUtil.importExcelMore(file.getInputStream(), pojoClass, params);
} catch (Exception e) {
//此處抛異常
}
if (result != null) {
this.checkTitleCell(result, params.getTitleRows(), reqDto.getTitleCells());
if (!CollectionUtils.isEmpty(result.getList())) {
this.findDuplicate(result.getList(), result.getFailList());
}
//開啟事務儲存資料,這種用法主要是為了解決事務失效的問題,見下面描述
transactionalHelper.apply(this::saveData, result);
}
}
//校驗标題格式是否正确
private void checkTitleCell(ExcelImportResult result, int titleRows, int titleCells) {
Row row = result.getWorkbook().getSheetAt(0).getRow(titleRows);
if (row.getLastCellNum() < titleCells) {
//此處抛異常
}
for (int i=0; i
Cell cell = row.getCell(i);
if (cell == null || StringUtils.isBlank(cell.getStringCellValue())) {
//此處抛異常
}
}
}
//該抽象方法主要是為了找出excel中重複的資料,重複的資料放在failList裡
protected abstract void findDuplicate(List importBos, List failList);
//該抽象方法儲存解析出來的資料
protected abstract void saveData(ExcelImportResult result);
}
代碼中簡化了很多東西,讀者可以自己去細化。代碼中用了一個TransactionalHelper,參見解決事務失效的工具類
2 導出
導出采用模闆方式導出,實體類還是用上文中的UserExcel。導出隻介紹封裝的抽象類
public abstract class AbstractExportService {
protected void exportExcel(List dataList, String templateUrl, String fileName, int startRows, boolean hasComment) {
Map resMap = new HashMap<>();
resMap.put("mapList", dataList);
try {
ClassPathResource classPathResource = new ClassPathResource(
templateUrl);
TemplateExportParams params = new TemplateExportParams(
classPathResource.getPath(), true);
Workbook workbook = ExcelExportUtil.exportExcel(params,resMap);
this.buildComment(dataList, workbook, startRows, hasComment);
//将workbook寫入到response裡,讀者自行實作
}catch (Exception e){
//此處抛異常
}
}
private void buildComment(List dataList, Workbook workbook, int startRows, boolean hasComment) {
if (!hasComment) return;
Sheet sheet = workbook.getSheetAt(0);
//建立一個圖畫工具
Drawing> drawing = sheet.createDrawingPatriarch();
for (T fail : dataList) {
Row row = sheet.getRow(startRows);
//擷取批注資訊
String commentStr = this.getCommentStr(fail);
if (StringUtils.isNotBlank(commentStr)) {
//解析批注,并傳換成map
Map commentMap = this.getCommentMap(commentStr);
for (Map.Entry entry : commentMap.entrySet()) {
Cell cell = row.getCell(entry.getKey());
//建立批注
Comment comment = drawing.createCellComment(this.newClientAnchor(workbook));
//輸入批注資訊
comment.setString(this.newRichTextString(workbook, entry.getValue()));
//将批注添加到單元格對象中
cell.setCellComment(comment);
//設定單元格背景顔色
CellStyle cellStyle = workbook.createCellStyle();
//設定顔色
cellStyle.setFillForegroundColor(IndexedColors.RED.getIndex());
//設定實心填充
cellStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);
cell.setCellStyle(cellStyle);
}
}
startRows++;
}
}
protected Map getCommentMap(String commentStr) {
//每行的所有單元格的批注都在commentStr裡,并用”,”分隔
String[] split = commentStr.split(",");
Map commentMap = new HashMap<>();
for (String msg : split) {
String[] cellMsg = msg.split("#");
//如果目前列沒有批注,會将該列的索引作為key存到map裡;已有批注,以“,“分隔繼續拼接
int cellIndex = Integer.parseInt(cellMsg[1]);
if (commentMap.get(cellIndex) == null) {
commentMap.put(cellIndex, cellMsg[0]);
} else {
commentMap.replace(cellIndex, commentMap.get(cellIndex) + "," + cellMsg[0]);
}
}
return commentMap;
}
private ClientAnchor newClientAnchor(Workbook workbook) {
//xls
if (workbook instanceof HSSFWorkbook) {
return new HSSFClientAnchor(0, 0, 0, 0, (short) 3, 3, (short) 5, 6);
}
//xlsx
else {
return new XSSFClientAnchor(0, 0, 0, 0, (short) 3, 3, (short) 5, 6);
}
}
private RichTextString newRichTextString(Workbook workbook, String msg) {
//xls
if (workbook instanceof HSSFWorkbook) {
return new HSSFRichTextString(msg);
}
//xlsx
else {
return new XSSFRichTextString(msg);
}
}
protected abstract String getCommentStr(T data);
導出模闆如下圖
excel中
$fe:表示循環插入
mapList是傳入map的key
本文位址:https://blog.csdn.net/weixin_45497155/article/details/107341472
如您對本文有疑問或者有任何想說的,請點選進行留言回複,萬千網友為您解惑!