一、文件导入
1.单个sheet 表格数据导入
基于alibaba.easyexcel实现,这里直接上实现代码
// conrtoller 层
@PostMapping("/import")
@ApiOperation(value = "批量导入区域信息")
public ObjectRestResponse<ImportResultVo> importCreate(
@RequestParam(value = "file") MultipartFile file) {
ImportResultVo resultVo = areaService.importCreate(file);
return new ObjectRestResponse<>().data(resultVo);
}
/**
* service 层
* @author: xuyahui
* @Date: 2022/3/9 0:56
* @Description: 批量导入区域信息
*/
public ImportResultVo importCreate(MultipartFile file) {
try {
ModelExcelListener<AreaExcelVo> listener = new ModelExcelListener<>();
EasyExcel.read(file.getInputStream(), AreaExcelVo.class,listener).sheet(0).doRead();
List<AreaExcelVo> importList = listener.getList();
if (CollectionUtils.isEmpty(importList)){
return ImportResultVo.buildFail("导入失败,文件没有数据");
}
AreaExcelVo firstVo = importList.get(0);
List<String> desc = EasyExcelUtil.getDesc(AreaExcelVo.class);
if (!CollectionUtils.isEmpty(desc) && desc.get(0).equals(firstVo.getCode())){
//如果是描述,则移除
importList.remove(0);
}
List<AreaExcelVo> checkedList = new ArrayList<>();// 验证通过的数据
List<ImportErrorVo> errorList = new ArrayList<>();// 验证失败的错误信息集合
for (AreaExcelVo importVo : importList) {
String errorInfo = ValidatorUtil.validateReturnFirstError(importVo);
if (StringUtils.hasText(errorInfo)){
errorList.add(ImportErrorVo.build(importVo.getRowIndex(),errorInfo));
continue;
}
checkedList.add(importVo);
}
if (CollectionUtils.isEmpty(checkedList)){
return ImportResultVo.buildFail(errorList);
}
//业务校验 区域编码是否存在
Map<String, AreaExcelVo> importAreaMap = checkedList.stream().collect(Collectors.toMap(AreaExcelVo::getCode,
Function.identity(),(k1, k2) -> k1));
List<Area> areaList = areaMapper.listAll();
Map<String, Area> areaMap = areaList.stream().collect(Collectors.toMap(Area::getCode, Function.identity(), (k1, k2) -> k2));
Iterator<String> iterator = importAreaMap.keySet().iterator();
while (iterator.hasNext()) {
String importKey = iterator.next();
if (areaMap.containsKey(importKey)){
//数据库已存在
AreaExcelVo importVo = importAreaMap.get(importKey);
errorList.add(ImportErrorVo.build(importVo.getRowIndex(),"区域编码重复"));
iterator.remove();
importAreaMap.remove(importKey);
}
}
if (!CollectionUtils.isEmpty(errorList)){
List<ImportErrorVo> sortedErrorList = errorList.stream().sorted(Comparator.comparing(ImportErrorVo::getRowIndex)).collect(Collectors.toList());
return ImportResultVo.buildFail(sortedErrorList);
}
List<Area> saveAreaList = new ArrayList<>();
for (Map.Entry<String, AreaExcelVo> entry : importAreaMap.entrySet()) {
AreaExcelVo excelVo = entry.getValue();
Area area = Area.buildImportEntity(excelVo);
area.setId(SnowflakeIdUtil.nextId());
saveAreaList.add(area);
}
areaMapper.batchInsert(saveAreaList);
return ImportResultVo.buildSuccess(importAreaMap.size());
}catch (Exception e){
log.error("区域导入异常 ==>{}",e.getMessage(),e);
return ImportResultVo.buildFail(e.getMessage());
}
}
下面是相关的验证和处理类
package com.spring.mes.common.util.easyexcel.listener;
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import com.alibaba.excel.exception.ExcelAnalysisException;
import com.alibaba.excel.exception.ExcelDataConvertException;
import com.spring.mes.common.util.easyexcel.EasyExcelUtil;
import com.spring.mes.pojo.msg.exception.BaseException;
import com.spring.mes.pojo.vo.basic.excel.ExcelModel;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.CollectionUtils;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* 实体类模板的导入
*
* @author maji
*/
@Slf4j
@Data
@EqualsAndHashCode(callSuper=false)
public class ModelExcelListener<T extends ExcelModel> extends AnalysisEventListener<T> {
//一次允许多少条数据导入
private int batchCount = 10000;
//用list集合保存解析到的结果
private List<T> list = new ArrayList<>();
//表头
private Map<Integer,Map<Integer,String>> headMap = new HashMap<>();
/**
* 取时,使用实体类读取表头,否则只会默认读第一行为表头
* EasyExcel.read(file.getInputStream(), 实体类.class,listener).sheet(0).doRead();
* @param excelHead
* @param context
*/
@Override
public void invokeHeadMap(Map<Integer, String> excelHead, AnalysisContext context) {
// 读取时,使用实体类读取表头,否则只会默认读第一行为表头
//EasyExcel.read(file.getInputStream(), 实体类.class,listener).sheet(0).doRead();
Integer rowIndex = context.readRowHolder().getRowIndex();
//生成表头并校验
Class tClass = context.currentReadHolder().excelReadHeadProperty().getHeadClazz();
if (CollectionUtils.isEmpty(headMap)){
headMap = EasyExcelUtil.getHeadMap(tClass);
}
//校验表头
Map<Integer, String> classHead = headMap.get(rowIndex);
if (!excelHead.equals(classHead)){
//此处异常会走到 onException方法,不管什么异常都会被转成 ExcelAnalysisException
throw new ExcelAnalysisException("表头校验异常,请使用正确的模板进行导入");
}
}
@Override
public void invoke(T data, AnalysisContext context) {
Integer rowIndex = context.readRowHolder().getRowIndex();
data.setRowIndex(rowIndex + 1);
list.add(data);
if (list.size() > batchCount) {
//此处异常会走到 onException方法,不管什么异常都会被转成 ExcelAnalysisException
throw new ExcelAnalysisException("导入条数超过限制,最多导入"+batchCount+"条");
}
}
/**
* 解析到最后会进入这个方法,需要则重写这个doAfterAllAnalysed方法
* @param context
*/
@Override
public void doAfterAllAnalysed(AnalysisContext context) {
if (CollectionUtils.isEmpty(list)){
//此处异常不会走到 onException 方法
throw new ExcelAnalysisException("导入失败,文件没有数据");
}
}
/**
* 在有异常时,抛出异常则停止读取。如果这里不抛出异常则 会继续读取下一行。
*/
@Override
public void onException(Exception exception, AnalysisContext context) throws Exception {
// 此处抛的异常都会在 AbstractReadHolder类中 转成 ExcelAnalysisException 异常
Integer rowIndex = context.readRowHolder().getRowIndex();
if (rowIndex == headMap.size()){
//如果是表头下一行的数据异常,放行,防止是因为 字段描述 造成的异常
return;
}
ExcelDataConvertException excelDataConvertException = null;
if (exception instanceof ExcelDataConvertException) {
excelDataConvertException = (ExcelDataConvertException) exception;
String errorConvert = String.format("第%s行第%s列,解析异常",excelDataConvertException.getRowIndex()+1,
excelDataConvertException.getColumnIndex()+1);
throw new ExcelAnalysisException(errorConvert);
} else{
throw exception;
}
}
}
package com.spring.mes.common.util.easyexcel;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.annotation.ExcelIgnore;
import com.alibaba.excel.annotation.ExcelProperty;
import com.alibaba.excel.write.builder.ExcelWriterBuilder;
import com.alibaba.excel.write.metadata.WriteSheet;
import com.alibaba.excel.write.metadata.style.WriteCellStyle;
import com.alibaba.excel.write.metadata.style.WriteFont;
import com.alibaba.excel.write.style.HorizontalCellStyleStrategy;
import com.spring.mes.common.util.FieldTool;
import com.spring.mes.common.util.easyexcel.handler.ColumnAutoWidthHandler;
import com.spring.mes.common.util.easyexcel.handler.HiddenSheetSpinnerWriteHandler;
import com.spring.mes.pojo.ExcelFieldDesc;
import com.spring.mes.pojo.msg.exception.BaseException;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.ss.usermodel.BorderStyle;
import org.apache.poi.ss.usermodel.IndexedColors;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.lang.reflect.Field;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.*;
/**
* easyExcel 简单工具类
*
* @author maji
*/
@Slf4j
public class EasyExcelUtil {
public static String[] specialCharArr = {"[", "]", "?", "*", "/", "\\"};
/**
* 获取最基础的ExcelWriter
*
* @param response
* @param fileName 导出文件名
*/
public static ExcelWriter getExcelWriter(HttpServletResponse response, String fileName) {
try {
return getExcelWriterBuilder(response, fileName)
.registerWriteHandler(new ColumnAutoWidthHandler())
.registerWriteHandler(new HorizontalCellStyleStrategy(styleHead(), styleCells()))
.build();
} catch (Exception e) {
log.error("getExcelWriter fail ==>{}", e);
}
return null;
}
/**
* 获取ExcelWriterBuilder
*
* @param response
* @param fileName 导出文件名
*/
public static ExcelWriterBuilder getExcelWriterBuilder(HttpServletResponse response, String fileName) {
try {
return EasyExcel.write(getServletOutputStream(response, fileName));
} catch (Exception e) {
log.error("getExcelWriterBuilder fail ==>{}", e);
}
return null;
}
/**
* 获取输出流
*
* @param response
* @param fileName
* @return
* @throws IOException
*/
public static ServletOutputStream getServletOutputStream(HttpServletResponse response, String fileName) throws IOException {
fileName = URLEncoder.encode(fileName, StandardCharsets.UTF_8.name());
response.setContentType("application/vnd.ms-excel");
response.setCharacterEncoding("UTF-8");
response.setHeader("Content-Disposition", "attachment; filename=" + fileName + ".xlsx");
response.setHeader("Pragma", "public");
response.setHeader("Cache-Control", "no-store");
response.addHeader("Cache-Control", "max-age=0");
return response.getOutputStream();
}
/**
* 导出模板
* @param response
* @param headClass
* @param fileSheetName
*/
public static void downloadTemplate(HttpServletResponse response,Class headClass,String fileSheetName) {
ExcelWriter excelWriter = null;
try {
excelWriter = EasyExcelUtil.getExcelWriter(response, fileSheetName);
WriteSheet writeSheet = EasyExcel.writerSheet()
.head(headClass)
.sheetName(fileSheetName)
.build();
//设置第二行为字段说明
excelWriter.write(Collections.singletonList(EasyExcelUtil.getDesc(headClass)), writeSheet);
excelWriter.finish();
}catch (Exception e) {
log.error("导出模板失败!", e);
throw new BaseException("导出模板失败");
}finally {
if (excelWriter != null) {
excelWriter.finish();
}
}
}
/**
* 导出模板,单元格中设置下拉框选项
*/
public static void downloadTemplate(HttpServletResponse response,Class headClass,String fileSheetName,Map<Integer, List<String>> dropDownMap) {
ExcelWriter excelWriter = null;
try {
//选项放下拉框,第一行表头,第2行字段说明,第三行开始放下拉框
HiddenSheetSpinnerWriteHandler spinnerWriteHandler = HiddenSheetSpinnerWriteHandler.builder().dropDownMap(dropDownMap).firstRow(2).build();
excelWriter = EasyExcelUtil.getExcelWriterBuilder(response, fileSheetName)
.registerWriteHandler(new ColumnAutoWidthHandler())
.registerWriteHandler(new HorizontalCellStyleStrategy(EasyExcelUtil.styleHead(), EasyExcelUtil.styleCells()))
.registerWriteHandler(spinnerWriteHandler)
.build();
WriteSheet writeSheet = EasyExcel.writerSheet()
.head(headClass)
.sheetName(fileSheetName)
.build();
//设置第二行为字段说明
excelWriter.write(Collections.singletonList(EasyExcelUtil.getDesc(headClass)), writeSheet);
excelWriter.finish();
}catch (Exception e) {
log.error("导出模板失败!", e);
throw new BaseException("导出模板失败");
}finally {
if (excelWriter != null) {
excelWriter.finish();
}
}
}
/**
* 导出固定表头的数据
*/
public static void exportData(HttpServletResponse response, String fileSheetName, Class headClass, List<? extends Object> dataList) {
ExcelWriter excelWriter = null;
try {
excelWriter = EasyExcelUtil.getExcelWriter(response, fileSheetName);
WriteSheet writeSheet = EasyExcel.writerSheet()
.head(headClass)
.sheetName(fileSheetName)
.build();
excelWriter.write(dataList, writeSheet);
excelWriter.finish();
}catch (Exception e) {
log.error("导出"+fileSheetName+"失败!", e);
throw new BaseException("导出数据失败");
}finally {
if (excelWriter != null) {
excelWriter.finish();
}
}
}
/**
* 表头样式
* */
public static WriteCellStyle styleHead() {
// 头的策略
WriteCellStyle headWriteCellStyle = new WriteCellStyle();
// 设置背景色
headWriteCellStyle.setFillForegroundColor(IndexedColors.LIGHT_GREEN.getIndex());
WriteFont headWriteFont = new WriteFont();
headWriteFont.setBold(true);
headWriteFont.setFontHeightInPoints((short)14);
headWriteCellStyle.setWriteFont(headWriteFont);
// 设置边框
setBorder(headWriteCellStyle);
return headWriteCellStyle;
}
/**
* 内容样式
* */
public static WriteCellStyle styleCells() {
// 内容的策略
WriteCellStyle contentWriteCellStyle = new WriteCellStyle();
return setBorder(contentWriteCellStyle);
}
/**
* 设置边框
* */
private static WriteCellStyle setBorder(WriteCellStyle style) {
style.setBorderTop(BorderStyle.THIN);
style.setBorderBottom(BorderStyle.THIN);
style.setBorderLeft(BorderStyle.THIN);
style.setBorderRight(BorderStyle.THIN);
return style;
}
/**
* 获取模板实体类中的说明
* @param clazz
* @return
*/
public static List<String> getDesc(Class clazz) {
List<Field> fields = FieldTool.getDeclaredFields(clazz);
List<String> descList = new ArrayList<>();
for (Field field : fields) {
ExcelProperty property = field.getAnnotation(ExcelProperty.class);
if (property != null) {
ExcelFieldDesc desc = field.getAnnotation(ExcelFieldDesc.class);
if (desc != null) {
descList.add(desc.value());
}else {
descList.add("");
}
}
}
return descList;
}
/**
* 从模板实体类中转换表头
* @param clazz
* @return
*/
public static Map<Integer,Map<Integer,String>> getHeadMap(Class clazz) {
List<Field> fields = FieldTool.getDeclaredFields(clazz);
Integer index = 0;
//正常只有一行表头,但可能也有多行,key 从0 开始
Map<Integer,Map<Integer,String>> headMap = new HashMap<>();
for (Field field : fields) {
ExcelIgnore ignore = field.getAnnotation(ExcelIgnore.class);
if (ignore != null){
continue;
}
ExcelProperty property = field.getAnnotation(ExcelProperty.class);
if (property != null) {
String[] values = property.value();
for (int i = 0; i < values.length; i++) {
Map<Integer, String> headRow = headMap.get(i);
if (headRow == null){
headRow = new HashMap<>();
headMap.put(i,headRow);
}
headRow.put(index,values[i]);
}
index ++ ;
}
}
return headMap;
}
/**
* sheetName 去除特殊字符
*
* @param sheetName
* @return
*/
public static String checkSheetName(String sheetName) {
for (String s : specialCharArr) {
sheetName = StringUtils.replace(sheetName, s, "");
}
return sheetName;
}
/**
* 自定义表头
*
* @param headList sheet表头
*/
public static List<List<String>> getHead(Collection<String> headList) {
List<List<String>> list = new ArrayList<>();
if (headList != null) {
headList.forEach(h -> list.add(Collections.singletonList(h)));
}
return list;
}
}
package com.spring.mes.common.util;
import lombok.experimental.UtilityClass;
import javax.validation.ConstraintViolation;
import javax.validation.Validation;
import javax.validation.Validator;
import java.util.List;
import java.util.stream.Collectors;
@UtilityClass
public class ValidatorUtil {
private final Validator validator;
static {
validator = Validation.buildDefaultValidatorFactory().getValidator();
}
public List<String> validate(Object object, Class<?>... groups) {
return validator.validate(object, groups)
.stream().map(ConstraintViolation::getMessage)
.collect(Collectors.toList());
}
public static String validateReturnFirstError(Object object){
return validator.validate(object)
.stream().findFirst().map(ConstraintViolation::getMessage)
.orElse(null);
}
}
package com.spring.mes.pojo.vo.basic.excel;
import com.alibaba.excel.annotation.ExcelIgnore;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
import java.util.List;
@Data
@NoArgsConstructor
public class ImportResultVo implements Serializable {
@ApiModelProperty(value = "是否导入成功")
private Boolean success;
@ApiModelProperty(value = "成功数量")
private Integer successNum;
@ApiModelProperty(value = "单独的异常信息")
private String errorMsg;
@ApiModelProperty(value = "异常行信息集合")
private List<ImportErrorVo> errorList;
@ApiModelProperty(value = "导入成功的对象集合")
private List<Object> list;
public static ImportResultVo buildFail(String errorMsg) {
ImportResultVo vo = new ImportResultVo();
vo.setSuccess(false);
vo.setErrorMsg(errorMsg);
return vo;
}
public static ImportResultVo buildFail(List<ImportErrorVo> errorList) {
ImportResultVo vo = new ImportResultVo();
vo.setSuccess(false);
vo.setErrorMsg("部分数据导入失败,请重新导入!");
vo.setErrorList(errorList);
return vo;
}
public static ImportResultVo buildSuccess(Integer successNum) {
ImportResultVo vo = new ImportResultVo();
vo.setSuccess(true);
vo.setSuccessNum(successNum);
return vo;
}
public static ImportResultVo buildItemList(List<Object> list) {
ImportResultVo vo = new ImportResultVo();
vo.setSuccess(true);
vo.setList(list);
vo.setSuccessNum(list.size());
return vo;
}
public static ImportResultVo buildErrAndOkItemList(List<ImportErrorVo> errorList,List<Object> list) {
ImportResultVo vo = new ImportResultVo();
vo.setSuccess(false);
vo.setErrorMsg("部分数据导入失败,请重新导入!");
vo.setErrorList(errorList);
vo.setSuccess(true);
vo.setList(list);
return vo;
}
}
2.多个sheet表格数据导入
@ApiOperation(value = "批量导入用户")
@PostMapping("/import")
@SysLog(value = "导入用户",module = LogModuleConstants.USER_CENTER,menu = LogMenuConstants.USER)
public ResponseBody importCreate(@RequestParam(value = "file",required = false) MultipartFile file) {
UserImportResultVo resultVo = userService.importCreate(file);
return ResponseBody.success(resultVo);
}
/**
* @author: xuyahui
* @Date: 2021/4/7 14:11
* @Description: 批量导入用户
*/
@Override
@Transactional(rollbackFor = Exception.class)
public UserImportResultVo importCreate(MultipartFile file) {
try {
InputStream in = file.getInputStream();
ExcelReader excelReader = EasyExcel.read(in).build();
PhoneListener phoneListener = new PhoneListener(this);
EmailListener emailListener = new EmailListener(this);
UsernameListener usernameListener = new UsernameListener(this);
//获取sheet对象
ReadSheet phoneSheet =
EasyExcel.readSheet(0).head(PhoneImportTemplate.class).registerReadListener(phoneListener).build();
ReadSheet emailSheet =
EasyExcel.readSheet(1).head(EmailImportTemplate.class).registerReadListener(emailListener).build();
ReadSheet usernameSheet =
EasyExcel.readSheet(2).head(UsernameImportTemplate.class).registerReadListener(usernameListener).build();
// 业务处理
excelReader.read(phoneSheet, emailSheet, usernameSheet);
return convertAndSave(phoneListener, emailListener, usernameListener);
} catch (IOException e) {
log.error("批量导入用户失败!");
}
return null;
}
/**
* @author: xuyahui
* @Date: 2021/5/8 15:49
* @Description: 转换并保存数据
*/
private UserImportResultVo convertAndSave(PhoneListener phoneListener,
EmailListener emailListener,
UsernameListener usernameListener) {
List<PhoneImportTemplate> phoneUserList = phoneListener.getData();
List<EmailImportTemplate> emailUserList = emailListener.getData();
List<UsernameImportTemplate> usernameUserList = usernameListener.getData();
// 验证表头
checkTableHead(phoneListener.getHeadMap(),emailListener.getHeadMap(),usernameListener.getHeadMap());
// 验证数据
checkImportUserInfo(phoneUserList, emailUserList, usernameUserList);
List<UserImportVo> userImportVoList = buildImportUserVoList(phoneUserList, emailUserList, usernameUserList);
SysOrganizationEntity company = organizationBiz.selectById(SessionBean.getTenantId());
// 处理数据
int successNum = 0;
List<SysOrganizationUserRel> organizationUserRels = null;
List<SysRoleUserRel> sysRoleUserRelList = null;
for (UserImportVo importUser : userImportVoList) {
// 查询组织机构和角色
organizationUserRels = new ArrayList<>();
sysRoleUserRelList = new ArrayList<>();
SysOrganizationEntity organization = organizationBiz.selectByOrgName(importUser.getOrganization());
SysRoleEntity roleEntity = sysRoleBiz.selectByNameAndTenantId(importUser.getRole(), SessionBean.getTenantId());
SysUserEntity user = ObjectCopyUtil.objectCopy(importUser, SysUserEntity.class);
initUser(user);
SysOrganizationUserRel companyUserRel = buildUserOrganizationRel(user.getId(), SessionBean.getTenantId(), null, SessionBean.getTenantId(), true);
organizationUserRels.add(companyUserRel);
if (!Objects.isNull(organization)) {
SysOrganizationUserRel orgRel = buildUserOrganizationRel(user.getId(), null, organization.getId(), SessionBean.getTenantId(), true);
organizationUserRels.add(orgRel);
}
if (!Objects.isNull(roleEntity)) {
SysRoleUserRel roleUserRel = SysRoleUserRel.create(user, roleEntity);
sysRoleUserRelList.add(roleUserRel);
}
// 查询验证角色是否有管理员
SysRoleEntity addAdminRole = getAdminRole(sysRoleUserRelList);
sysUserBiz.insertUser(user, organizationUserRels, sysRoleUserRelList, companyUserRel);
// 平台创建
try {
createPlatUser(user, company, organizationUserRels);
if (Objects.nonNull(addAdminRole)) {// 新增组织管理员
aclServiceFeign.addUserToOrganizationAdmin(SessionBean.getTenantId(), user.getId());
}
successNum++;
} catch (Exception e) {
sysUserBiz.deleteMetaUserById(user.getId());
sysUserBiz.deleteUserById(user.getId());
}
}
return UserImportResultVo.build(userImportVoList.size(), successNum);
}
3.带参数的文件导入
除了文件,还有对象信息的参数,请求参数可以放在form-data里面,具体示例如下:
(1)controller 类中代码写法
/**
* 上传模型
* @param abFile
* @param jpgFile
* @param sxEquipment
* @return
* @throws IOException
*/
@PreAuthorize("@ss.hasAnyPermi('asset:workshop:add,asset:device:add,asset:facilities:add')")
@PostMapping("/addEquipment")
public AjaxResult addEquipment(MultipartFile abFile, MultipartFile jpgFile,SxEquipment sxEquipment) throws IOException {
try {
List<SxEquipment> sxEquipmentlist = sxEquipmentService.selectListEq(sxEquipment);
if (sxEquipmentlist.size() > 0) return AjaxResult.error("设备已存在");
sxEquipment.setModelTime(DateUtils.getNowDate());
// sxEquipment.setCreateBy(SecurityUtils.getLoginUser().getUser().getUserName());
sxEquipment.setThumbTime(DateUtils.getNowDate());
sxEquipment.setCreateFlag("0");
sxEquipment.setBrowseNum(0);
sxEquipment.setDownloadNum(0);
// sxEquipment.setCompanyName("火星视觉数字科技有限公司");
sxEquipment.setCreateTime(DateUtils.getNowDate());
sxEquipmentService.save(sxEquipment);
if (Objects.nonNull(abFile)) {
// String abUrl = FileUploadUtils.mixstationA(abFile, 1, sxEquipment.getEquipmentId());
String abUrl = ossService.mixstation1(abFile, 1, sxEquipment.getEquipmentId());
sxEquipment.setModel(abUrl);
}
String jpgUrl = ossService.mixstation1(jpgFile, 0, sxEquipment.getEquipmentId());
sxEquipment.setPic(jpgUrl);
sxEquipmentService.updateEquipmentById(sxEquipment);
if (Objects.nonNull(sxEquipment.getPic())) sxEquipment.setThumbTime(DateUtils.getNowDate());
return AjaxResult.success();
} catch (Exception e) {
throw new IOException(e.getMessage(), e);
}
}
(2)apifox请求示例:
二、文件导出
// controller 层
@PostMapping(value = "/export")
@ApiOperation(value = "区域信息导出")
public void export(HttpServletResponse response, @RequestBody AreaQueryDto dto){
log.info("区域信息导出==>{}", JSON.toJSONString(dto));
areaService.export(response,dto);
}
/**
* @author: xuyahui
* @Date: 2022/3/4 11:16
* @Description: 区域信息导出
*/
public void export(HttpServletResponse response, AreaQueryDto dto) {
List<AreaExcelVo> resultList = getExportResultList(dto);
String fileSheetName = "区域";
ExcelWriter excelWriter = null;
try {
excelWriter = EasyExcelUtil.getExcelWriter(response, fileSheetName+"_"+ DateUtils.format(new Date()));
WriteSheet writeSheet = EasyExcel.writerSheet()
.head(AreaExcelVo.class)
.sheetName(fileSheetName)
.build();
excelWriter.write(resultList, writeSheet);
excelWriter.finish();
}catch (Exception e) {
log.error("导出区域信息失败!", e);
throw new BaseException("导出区域信息失败");
}finally {
if (excelWriter != null) {
excelWriter.finish();
}
}
}
/**
* @author: xuyahui
* @Date: 2022/3/4 11:25
* @Description: 获取区域导出信息
*/
private List<AreaExcelVo> getExportResultList(AreaQueryDto dto) {
List<Area> areaList = areaMapper.filterArea(dto);
if(CollectionUtils.isEmpty(areaList)){
return Lists.newArrayList();
}
List<AreaExcelVo> areaExcelVoList = areaList.stream().map(v->v.buildExportVo()).collect(Collectors.toList());
return areaExcelVoList;
}
三、下载导入模板
1.多个sheet实现
@ApiOperation(value = "下载用户导入模板")
@GetMapping("/template")
public void downloadTemplate(HttpServletResponse response) {
log.info("下载用户导入模板");
userService.downloadTemplate(response);
}
@Override
public void downloadTemplate(HttpServletResponse response) {
try {
ResourceBundle resourceBundle = i18nSet();
String fileName = URLEncoder.encode(resourceBundle.getString("EXCEL_USER_TEMPLATE"), "UTF-8");
// String fileName = URLEncoder.encode("用户导入模板", "UTF-8");
response.setContentType("application/vnd.ms-excel");
response.setCharacterEncoding("UTF-8");
response.setHeader("Content-disposition", "attachment;filename=" + fileName + ".xlsx");
ExcelWriter excelWriter = EasyExcel.write(response.getOutputStream()).build();
List<SysRoleEntity> sysRoleEntityList = sysRoleBiz.listAll(SessionBean.getTenantId());
List<String> roleNameList = sysRoleEntityList.stream()
.filter(role -> (0 != role.getRoleType() && 1 != role.getRoleType()))
.map(SysRoleEntity::getRoleName).collect(Collectors.toList());
Set<String> authorizedOrgIdSet = userService.getUserAuthorizedOrgIdList(SessionBean.getUserId());
List<SysOrganizationEntity> organizationList = organizationBiz.listByIds(authorizedOrgIdSet);
List<String> organizationNameList = organizationList.stream().map(SysOrganizationEntity::getOrganizationName).collect(Collectors.toList());
WriteSheet phoneSheet = buildPhoneSheet(roleNameList, organizationNameList,resourceBundle);
WriteSheet emailSheet = buildEmailSheet(roleNameList, organizationNameList,resourceBundle);
WriteSheet usernameSheet = buildUserNameSheet(roleNameList, organizationNameList,resourceBundle);
String displayNameDesc = resourceBundle.getString("EXCEL_DISPLAYNAME_DESC");
String phoneDesc = resourceBundle.getString("EXCEL_HPONE_DESC");
String emailDesc = resourceBundle.getString("EXCEL_EMAIL_DESC");
String userNameDesc = resourceBundle.getString("EXCEL_USERNAME_DESC");
String pwdDesc = resourceBundle.getString("EXCEL_PASSWORD_DESC");
List<String> phoneDescs = Arrays.asList(displayNameDesc, phoneDesc, "", "");
List<String> emailDescs = Arrays.asList(displayNameDesc, emailDesc, "", "");
List<String> usernameDescs = Arrays.asList(displayNameDesc, userNameDesc, pwdDesc, "", "");
// 写入数据
excelWriter.write(Collections.singletonList(phoneDescs), phoneSheet)
.write(Collections.singletonList(emailDescs), emailSheet)
.write(Collections.singletonList(usernameDescs), usernameSheet);
excelWriter.finish();
} catch (Exception e) {
log.error("[excelExport] error:", e);
}
}
四、文件上传
1.文件上传实现方法一(基于Minio实现)
@PostMapping(value = "/upload")
@ApiOperation(value = "上传文件返回文件存储名称", httpMethod = "POST", notes = "上传文件返回文件存储名称")
public String upload(@RequestParam("file") MultipartFile file,
@RequestParam("bucketName") String bucketName,
@RequestParam("childBucketName") String childBucketName,
@RequestParam(required = false) Boolean previewFlag) throws Exception {
log.info("上传文件返回可下载地址,fileName{},businessName{}",childBucketName);
return minioYunService.uploadFile(file,bucketName,childBucketName,previewFlag);
}
public String uploadFile(MultipartFile file,
String bucketName,
String childBucketName,
Boolean previewFlag) throws Exception {
String fileName = file.getOriginalFilename();
fileName = fileName.substring(Constants.ZERO,fileName.lastIndexOf(Constants.POINT)) + Constants.UNDERLINE
+ System.currentTimeMillis() + Constants.POINT
+ fileName.substring(fileName.lastIndexOf(Constants.POINT) + Constants.ONE);
return upload(file, fileName, Constants.CONTENT_TYPE_STREAM,bucketName,childBucketName, previewFlag);
}
private String upload(MultipartFile file, String fileName, String contentType, String bucketName,String childBucketName, Boolean previewFlag) throws Exception {
previewFlag = null == previewFlag ? true : previewFlag;
log.info("上传文件参数:fileName:{},bucketName:{},childBucketName:{}",fileName,bucketName,childBucketName);
client = new MinioClient(constants.getEndpoint(), constants.getAccessKey(), constants.getSecretKey());
String objectName = Objects.isNull(childBucketName) ? fileName : childBucketName + "/" + fileName;
//创建文件流
InputStream inputStream = file.getInputStream();
log.info("参数 文件夹名称+文件名称"+objectName);
try {
boolean isExist = client.bucketExists(bucketName);
if (isExist) {
log.debug("桶[{}]已经存在", bucketName);
} else {
//新建桶
client.makeBucket(bucketName);
}
if (!previewFlag) {
client.putObject(bucketName, objectName, inputStream, contentType);
} else {
client.putObject(bucketName, objectName, inputStream, FileContentTypeUtil.getContentType(fileName));
}
inputStream.close();
} catch (Exception e) {
log.error("file update error" + e);
}
return objectName;
}
public class FileContentTypeUtil {
public static String getContentType(String fileName) {
String type = fileName.substring(fileName.lastIndexOf(".")+1);
String contentType = Constants.CONTENT_TYPE_STREAM;
if (StringUtils.isNotBlank(type)) {
if (type.equals("jpg") || type.equals("jpeg")) {
contentType = "image/jpeg";
}
if (type.equals("png")) {
contentType = "image/png";
}
if (type.equals("gif")) {
contentType = "image/gif";
}
if (type.equals("mp4")) {
contentType = "video/mp4";
}
if (type.equals("wmv")) {
contentType = "video/x-ms-wmv";
}
if (type.equals("ogg")) {
contentType = "video/x-theora+ogg";
}
}
return contentType;
}
}
2.文件上传实现方法二(基于Minio实现)
controller 层代码
@RequestMapping(value = "/publicUpload", method = RequestMethod.POST)
@ApiOperation(value = "上传文件接口", notes = "")
@ApiImplicitParam(paramType = "header", dataType = "String", name = "workspaceId", value = "工作空间id", required = true)
@ResponseBody
public ObjectRestResponse<DsFileVo> publicUploadFile(@ApiParam("上传文件") @RequestParam("file") MultipartFile multipartFile,
@ApiParam("文件名称") @RequestParam(value = "fileName", required = false)
@Length(max = ValidatorConstant.COMMON_LENGTH_64, message = "文件名称" + ValidatorConstant.COMMON_LENGTH_64_MSG)
@Pattern(regexp = ValidatorConstant.CONVENTIONAL_TEXT_REG, message = "文件名称只能包含" + ValidatorConstant.CONVENTIONAL_TEXT_REG_MSG)
String fileName,
@ApiParam("存储文件加夹名字") @RequestParam(value = "folderName", required = false) String folderName
) throws IOException, XmlPullParserException, NoSuchAlgorithmException, RegionConflictException, InvalidKeyException, InvalidPortException, InvalidArgumentException, ErrorResponseException, NoResponseException, InvalidBucketNameException, InsufficientDataException, InvalidEndpointException, InternalException, InvalidObjectPrefixException {
if (null == multipartFile) {
throw new BaseException("上传文件不能为空", ErrorType.ERROR_REQUEST_FAILD.getIndex());
}
DsFileVo fileVo = fileService.publicUploadFile(multipartFile, fileName,folderName);
return new ObjectRestResponse().data(fileVo);
}
service 层代码
/**
*
* @param multipartFile
* @param fileName 文件名
* @param folderName Minio服务存储文件夹的文件夹名
* @return
*/
public DsFileVo publicUploadFile(MultipartFile multipartFile, String fileName,String folderName) throws IOException, InvalidKeyException, NoSuchAlgorithmException, XmlPullParserException, InvalidPortException, InvalidArgumentException, InternalException, ErrorResponseException, NoResponseException, InvalidBucketNameException, InsufficientDataException, InvalidEndpointException, RegionConflictException, InvalidObjectPrefixException{
String fileOriginalName = multipartFile.getOriginalFilename();
if (StringUtils.isEmpty(fileOriginalName)) {
throw new BaseException("文件原始名称缺失");
}
String extension = fileUtil.checkFileExtension(fileOriginalName);
DsFileVo dsFileVo = new DsFileVo();
String fileAbsolutePath = getFileAbsolutePath(folderName, fileOriginalName);
String fileRelativePath = getFileRelativePath(fileAbsolutePath);
InputStream ins = multipartFile.getInputStream();
File targetFile = null;
try {
targetFile = new File(fileRelativePath.substring(fileRelativePath.lastIndexOf("/") + 1));
FileUtils.copyInputStreamToFile(ins, targetFile);
String fileSize = fileUtil.getFileSize(targetFile);
fileUtil.saveMultiFile(targetFile.getName(), server, multipartFile, folderName+ "/" + LocalDate.now().toString());
DsFile dsFile = initLocalDsFile1(fileOriginalName, fileName, fileSize, fileRelativePath, extension);
ObjectCopyUtil.objectPropertiesCopy(dsFile, dsFileVo);
} catch (Exception ex) {
fileUtil.deleteFile(folderName,fileRelativePath);
throw ex;
} finally {
ins.close();
if (targetFile != null && targetFile.exists()) {
targetFile.delete();
}
}
return dsFileVo;
}
/**
* 获取文件存放绝对路径
*
* @param workspaceId
* @param fileOriginalName
* @return
*/
public String getFileAbsolutePath(String workspaceId, String fileOriginalName) {
String filePath = getFileParentPath(workspaceId) + "/" + fileUtil.getFileRandomName(fileOriginalName);
return filePath;
}
/**
* 获取空间文档默认存放路径
*
* @param workspaceId
* @return
*/
public String getFileParentPath(String workspaceId) {
String path = fileUtil.getDefaultRootPath() + "/" + server + "/" + workspaceId + "/" + LocalDate.now().toString();
File temp = new File(path);
if (!temp.exists()) {
if (!temp.mkdirs()) {
throw new BaseException("创建文件存储路径失败");
}
}
return temp.getAbsolutePath();
}
/**
* 根据文件原始名称,获取一个随机名称:xxx-xx...x-xxxx.txt
*
* @param fileOriginalName
* @return
*/
public String getFileRandomName(String fileOriginalName) {
String extension = StringUtils.getFilenameExtension(fileOriginalName);
String resourceId = UUID.randomUUID().toString();
if (StringUtils.isEmpty(extension)) {
return resourceId;
} else {
return resourceId + "." + extension;
}
}
/**
* 从空间开始的相对路径
*
* @param fileAbsolutePath
* @return
*/
public String getFileRelativePath(String fileAbsolutePath) {
String defaultRootPath = fileUtil.getDefaultRootPath();
if (fileAbsolutePath.contains(defaultRootPath)) {
return fileAbsolutePath.replace(defaultRootPath, "").replaceAll("\\\\", "/");
} else {
throw new BaseException("获取文件相对路径失败");
}
}
/**
* 获取默认路径
*
* @return
*/
public String getDefaultRootPath() {
String rootJarPath = getBaseJarPath().getAbsolutePath();
File defaultFile = new File(rootJarPath + "/" + DEFAULT_PATH);
if (!defaultFile.exists()) {
if (!defaultFile.mkdirs()) {
throw new BaseException("创建默认存放路径失败", ErrorType.ERROR.getIndex());
}
}
return defaultFile.getAbsolutePath();
}
minio文件存储实现
public void saveMultiFile(String fileName, String space, MultipartFile file, String filePath) throws IOException, XmlPullParserException, NoSuchAlgorithmException, RegionConflictException, InvalidKeyException, InvalidPortException, InvalidArgumentException, ErrorResponseException, NoResponseException, InvalidBucketNameException, InsufficientDataException, InvalidEndpointException, InternalException, InvalidObjectPrefixException {
minioUtil.saveFile(fileName, space, file, filePath);
}
public void saveFile(String fileName, String space, MultipartFile file, String filePath) throws InvalidPortException, InvalidEndpointException, IOException, InvalidKeyException, NoSuchAlgorithmException, InsufficientDataException, InternalException, NoResponseException, InvalidBucketNameException, XmlPullParserException, ErrorResponseException, RegionConflictException, InvalidArgumentException, InvalidObjectPrefixException {
boolean isExist = template.getMinioClient().bucketExists(space);
if (!isExist) {
template.createBucket(space);
}
template.getMinioClient().setBucketPolicy(space,filePath,PolicyType.NONE);
template.saveObject(space , filePath + "/" + fileName ,file.getInputStream(),file.getSize(),file.getContentType());
}
package com.spring.doc.minio.service;
import com.spring.doc.minio.vo.MinioItem;
import io.minio.MinioClient;
import io.minio.ObjectStat;
import io.minio.Result;
import io.minio.errors.*;
import io.minio.messages.Bucket;
import io.minio.messages.Item;
import org.xmlpull.v1.XmlPullParserException;
import java.io.*;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
public class MinioTemplate {
private String endpoint, accessKey, secretKey;
public MinioTemplate() {
}
/**
* Create new instance of the {@link MinioTemplate} with the access key and secret key.
*
* @param endpoint minio URL, it should be a URL, domain name, IPv4 address or IPv6 address
* @param accessKey uniquely identifies a minio account.
* @param secretKey the password to a minio account.
*/
public MinioTemplate(String endpoint, String accessKey, String secretKey) {
this.endpoint = endpoint;
this.accessKey = accessKey;
this.secretKey = secretKey;
}
/**
* Bucket Operations
*/
public void createBucket(String bucketName) throws XmlPullParserException, NoSuchAlgorithmException, InvalidKeyException, IOException, InvalidPortException, InvalidEndpointException, RegionConflictException, NoResponseException, InternalException, ErrorResponseException, InsufficientDataException, InvalidBucketNameException {
MinioClient client = getMinioClient();
if (!client.bucketExists(bucketName)) {
client.makeBucket(bucketName);
}
}
public List<Bucket> getAllBuckets() throws InvalidPortException, InvalidEndpointException, IOException, InvalidKeyException, NoSuchAlgorithmException, InsufficientDataException, InternalException, NoResponseException, InvalidBucketNameException, XmlPullParserException, ErrorResponseException {
return getMinioClient().listBuckets();
}
public Optional<Bucket> getBucket(String bucketName) throws InvalidPortException, InvalidEndpointException, IOException, InvalidKeyException, NoSuchAlgorithmException, InsufficientDataException, InternalException, NoResponseException, InvalidBucketNameException, XmlPullParserException, ErrorResponseException {
return getMinioClient().listBuckets().stream().filter(b -> b.name().equals(bucketName)).findFirst();
}
public void removeBucket(String bucketName) throws InvalidPortException, InvalidEndpointException, IOException, InvalidKeyException, NoSuchAlgorithmException, InsufficientDataException, InternalException, NoResponseException, InvalidBucketNameException, XmlPullParserException, ErrorResponseException {
getMinioClient().removeBucket(bucketName);
}
public List<MinioItem> getAllObjectsByPrefix(String bucketName, String prefix, boolean recursive) throws InvalidPortException, InvalidEndpointException {
List objectList = new ArrayList();
Iterable<Result<Item>> objectsIterator = getMinioClient().listObjects(bucketName, prefix, recursive);
objectsIterator.forEach(i -> {
try {
objectList.add(new MinioItem(i.get()));
} catch (Exception e) {
new Exception(e);
}
});
return objectList;
}
/**
* Object operations
*/
public String getObjectURL(String bucketName, String objectName, Integer expires) throws InvalidPortException, InvalidEndpointException, IOException, InvalidKeyException, NoSuchAlgorithmException, InsufficientDataException, InvalidExpiresRangeException, InternalException, NoResponseException, InvalidBucketNameException, XmlPullParserException, ErrorResponseException {
return getMinioClient().presignedGetObject(bucketName, objectName, expires);
}
public void saveObject(String bucketName, String objectName, InputStream stream, long size, String contentType) throws InvalidPortException, InvalidEndpointException, IOException, InvalidKeyException, NoSuchAlgorithmException, InsufficientDataException, InvalidArgumentException, InternalException, NoResponseException, InvalidBucketNameException, XmlPullParserException, ErrorResponseException {
getMinioClient().putObject(bucketName, objectName, stream, size, contentType);
}
// public void saveObject(String bucketName, String objectName, InputStream stream, long size, String contentType) throws InvalidPortException, InvalidEndpointException, IOException, InvalidKeyException, NoSuchAlgorithmException, InsufficientDataException, InvalidArgumentException, InternalException, NoResponseException, InvalidBucketNameException, XmlPullParserException, ErrorResponseException {
// getMinioClient().putObject(bucketName, objectName, stream, size, contentType);
// }
public ObjectStat getObjectInfo(String bucketName, String objectName) throws InvalidPortException, InvalidEndpointException, IOException, InvalidKeyException, NoSuchAlgorithmException, InsufficientDataException, InternalException, NoResponseException, InvalidBucketNameException, XmlPullParserException, ErrorResponseException {
return getMinioClient().statObject(bucketName, objectName);
}
public File getObject(String bucketName, String objectName ,String originName) throws IOException, NoSuchAlgorithmException, InvalidKeyException, XmlPullParserException {
File file = new File(originName);
try {
OutputStream os = new FileOutputStream(file);
getMinioClient().statObject(bucketName, objectName);
// 获取"myobject"的输入流。
InputStream stream = getMinioClient().getObject(bucketName, objectName);
// 读取输入流直到EOF并打印到控制台。
byte[] buf = new byte[16384];
int bytesRead = 0;
while ((bytesRead = stream.read(buf, 0, 8192)) != -1) {
os.write(buf, 0, bytesRead);
}
os.close();
// 关闭流,此处为示例,流关闭最好放在finally块。
stream.close();
} catch (MinioException e) {
System.out.println("Error occurred: " + e);
}
return file;
}
public void removeObject(String bucketName, String objectName) throws InvalidPortException, InvalidEndpointException, IOException, InvalidKeyException, NoSuchAlgorithmException, InsufficientDataException, InternalException, NoResponseException, InvalidBucketNameException, XmlPullParserException, ErrorResponseException, InvalidArgumentException {
getMinioClient().removeObject(bucketName, objectName);
}
/**
* Gets a Minio client
*
* @return an authenticated Amazon S3 client
*/
public MinioClient getMinioClient() throws InvalidPortException, InvalidEndpointException {
return new MinioClient(endpoint, accessKey, secretKey);
}
}
3.获取文件预览和下载地址
@GetMapping(value = "/getFilePresignedUrl")
@ApiOperation(value = "获取文件浏览或下载预签地址", httpMethod = "Get", notes = "获取文件浏览或下载预签地址,有失效时间")
public String getFilePresignedUrl(@RequestParam("bucketName") String bucketName,
@RequestParam("objectName") String objectName) throws Exception {
log.info("获取文件浏览或下载预签地址,bucketName{},objectName{}",bucketName,objectName);
return minioYunService.getFilePresignedUrl(bucketName,objectName);
}
/**
* @author: xuyahui
* @Date: 2021/5/27 9:45
* @Description: 根据文件存储名称,获取文件浏览或下载预签地址
*/
@Override
public String getFilePresignedUrl(String bucketName, String objectName) {
try {
client = new MinioClient(constants.getEndpointViewUrl(), constants.getAccessKey(), constants.getSecretKey());
boolean isExist = client.bucketExists(bucketName);
if(isExist){
return client.presignedGetObject(bucketName, objectName, constants.getExpiresTime());
}
} catch (Exception e) {
log.error("get presigned url error" + e);
}
return "";
}
五、本地写入文件
开发中,有些场景需要将文件写入到本地,再进行后续的操作,比如上传等,文件写入本地的时候,注意创建文件夹和文件的顺序。建议先创建文件夹,再创建文件,避免系统错误。
// 推荐的做法
String path = RuoYiConfig.getUploadPath()+"\\mixstation\\"+uid+"\\"+projectId+".sdt";
File file = new File(path);
if (!file.exists()) {
file.getParentFile().mkdirs();
}
file.createNewFile();
// 会出错的实现,windows server 2013 会提示文件目录不存在
String pathName = RuoYiConfig.getUploadPath()+"\\mixstation\\"+authorId+"\\"+projectId+".sdt";
File file = new File(pathName);
if (!file.exists()) {
file.createNewFile();
}