+ 文件组件代码重构

This commit is contained in:
mazhicheng 2020-02-21 12:07:12 +08:00
parent c9e2c934dc
commit 6ffae82640
43 changed files with 2524 additions and 14 deletions

View File

@ -0,0 +1,82 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>diboot-root</artifactId>
<groupId>com.diboot</groupId>
<version>2.0.4</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>diboot-component-file-starter</artifactId>
<packaging>jar</packaging>
<description>diboot component project - file</description>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<!-- 依赖core-starter -->
<dependency>
<groupId>com.diboot</groupId>
<artifactId>diboot-core-spring-boot-starter</artifactId>
<version>2.0.4</version>
</dependency>
<!-- 文件上传 -->
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.4</version>
</dependency>
<!-- Excel处理 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>easyexcel</artifactId>
<version>2.1.6</version>
</dependency>
<!-- http -->
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
<version>4.3.1</version>
</dependency>
<!-- 图片压缩 -->
<dependency>
<groupId>net.coobird</groupId>
<artifactId>thumbnailator</artifactId>
<version>0.4.9</version>
</dependency>
</dependencies>
<build>
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.xml</include>
<include>**/*.dtd</include>
</includes>
<filtering>false</filtering>
</resource>
<resource>
<directory>src/main/resources</directory>
</resource>
</resources>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>8</source>
<target>8</target>
</configuration>
</plugin>
</plugins>
</build>
</project>

View File

@ -0,0 +1,21 @@
package com.diboot.component.file.config;
/**
* 文件组件相关常量定义
* @author mazc@dibo.ltd
* @version v2.0
* @date 2020/02/18
*/
public class Cons {
/**
* 文件路径分隔符
*/
public static final String FILE_PATH_SEPARATOR = "/";
public enum FILE_STATUS {
S,
F
}
}

View File

@ -0,0 +1,148 @@
package com.diboot.component.file.controller;
import com.diboot.component.file.excel.listener.FixedHeadExcelListener;
import com.diboot.component.file.util.ExcelHelper;
import com.diboot.component.file.util.FileHelper;
import com.diboot.core.config.BaseConfig;
import com.diboot.core.exception.BusinessException;
import com.diboot.core.util.S;
import com.diboot.core.util.V;
import com.diboot.core.vo.JsonResult;
import com.diboot.core.vo.Status;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* Excel导入基类Controller
* @author Mazc@dibo.ltd
* @version 2.0
* @date 2020/02/20
*/
@Slf4j
public abstract class BaseExcelFileController extends BaseFileController {
// 预览文件名参数
protected static final String PREVIEW_FILE_NAME = "previewFileName";
/***
* 获取对应的ExcelDataListener
* @return
*/
protected abstract FixedHeadExcelListener getExcelDataListener();
/***
* excel数据预览
* @param request
* @return
* @throws Exception
*/
public JsonResult excelPreview(MultipartFile file, HttpServletRequest request) throws Exception {
Map<String, Object> dataMap = new HashMap();
savePreviewExcelFile(file, request, dataMap);
return new JsonResult(Status.OK, dataMap);
}
/***
* 预览后提交保存
* @param request
* @param
* @return
* @throws Exception
*/
public <T> JsonResult excelPreviewSave(Class<T> entityClass, String previewFileName, String originFileName, HttpServletRequest request) throws Exception {
if(V.isEmpty(previewFileName) || V.isEmpty(originFileName)){
throw new BusinessException(Status.FAIL_INVALID_PARAM, "预览保存失败,参数 tempFileName 或 originFileName 未指定!");
}
String fileUid = S.substringBefore(previewFileName, ".");
String fullPath = FileHelper.getFullPath(previewFileName);
String ext = FileHelper.getFileExtByName(originFileName);
String description = getString(request, "description");
// 保存文件上传记录
createUploadFile(entityClass, fileUid, originFileName, fullPath, ext, description);
return new JsonResult(Status.OK);
}
/***
* 直接上传excel
* @param request
* @param
* @return
* @throws Exception
*/
public <T> JsonResult uploadExcelFile(MultipartFile file, Class<T> entityClass, HttpServletRequest request) throws Exception {
checkIsExcel(file);
return uploadFile(file, entityClass, request);
}
/**
* 保存上传文件
* @param request
* @return
* @throws Exception
*/
private void savePreviewExcelFile(MultipartFile file, HttpServletRequest request, Map<String, Object> dataMap) throws Exception{
checkIsExcel(file);
// 文件后缀
String fileName = file.getOriginalFilename();
String ext = FileHelper.getFileExtByName(fileName);
// 先保存文件
String newFileName = S.newUuid() + "." + ext;
String fullPath = FileHelper.saveFile(file, newFileName);
// 预览
FixedHeadExcelListener listener = getExcelDataListener();
List dataList = null;
try {
dataList = ExcelHelper.previewRead(fullPath, listener);
}
catch (Exception e) {
log.warn("解析并校验excel文件失败", e);
throw new Exception(e.getMessage().replaceAll("; ", "<br/>"));
}
// 绑定属性到model
dataMap.put("header", listener.getHeadList());
dataMap.put(PREVIEW_FILE_NAME, newFileName);
if(V.notEmpty(dataList) && dataList.size() > BaseConfig.getPageSize()){
dataList = dataList.subList(0, BaseConfig.getPageSize());
}
//最多返回前端十条数据
dataMap.put("dataList", dataList);
}
/**
* 保存文件之后的处理逻辑如解析excel
*/
@Override
protected int extractDataCount(String fileUuid, String fullPath) throws Exception{
FixedHeadExcelListener listener = getExcelDataListener();
listener.setUploadFileUuid(fileUuid);
try{
ExcelHelper.readAndSave(fullPath, listener);
}
catch(Exception e){
log.warn("上传数据错误: "+ e.getMessage(), e);
throw new Exception(e.getMessage().replaceAll("; ", "<br/>"));
}
return listener.getDataList().size();
}
/**
* 检查是否为合法的excel文件
* @param file
* @throws Exception
*/
private void checkIsExcel(MultipartFile file) throws Exception{
if(V.isEmpty(file)) {
throw new BusinessException(Status.FAIL_INVALID_PARAM, "未获取待处理的excel文件");
}
String fileName = file.getOriginalFilename();
if (V.isEmpty(fileName) || !FileHelper.isExcel(fileName)) {
log.debug("非Excel类型: " + fileName);
throw new BusinessException(Status.FAIL_VALIDATION, "请上传合法的Excel格式文件");
}
}
}

View File

@ -0,0 +1,105 @@
package com.diboot.component.file.controller;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.diboot.component.file.entity.UploadFile;
import com.diboot.component.file.service.UploadFileService;
import com.diboot.component.file.util.FileHelper;
import com.diboot.core.controller.BaseController;
import com.diboot.core.exception.BusinessException;
import com.diboot.core.util.S;
import com.diboot.core.util.V;
import com.diboot.core.vo.JsonResult;
import com.diboot.core.vo.Pagination;
import com.diboot.core.vo.Status;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletRequest;
import java.util.List;
/**
* Excel导入基类Controller
* @author Mazc@dibo.ltd
* @version 2.0
* @date 2020/02/20
*/
@Slf4j
public abstract class BaseFileController extends BaseController {
@Autowired
protected UploadFileService uploadFileService;
/***
* 获取文件上传记录
* <p>
* url参数示例: /${bindURL}?pageSize=20&pageIndex=1
* </p>
* @return JsonResult
* @throws Exception
*/
protected JsonResult getEntityListWithPaging(Wrapper queryWrapper, Pagination pagination) throws Exception {
// 查询当前页的数据
List entityList = uploadFileService.getEntityList(queryWrapper, pagination);
// 返回结果
return new JsonResult(Status.OK, entityList).bindPagination(pagination);
}
/***
* 直接上传文件
* @param request
* @param
* @return
* @throws Exception
*/
public <T> JsonResult uploadFile(MultipartFile file, Class<T> entityClass, HttpServletRequest request) throws Exception {
if(file == null) {
throw new BusinessException(Status.FAIL_INVALID_PARAM, "未获取待处理的文件!");
}
String originFileName = file.getOriginalFilename();
if (V.isEmpty(originFileName) || !FileHelper.isValidFileExt(originFileName)) {
log.debug("非法的文件类型: " + originFileName);
throw new BusinessException(Status.FAIL_VALIDATION, "请上传合法的文件格式!");
}
// 文件后缀
String ext = FileHelper.getFileExtByName(originFileName);
// 先保存文件
String fileUid = S.newUuid();
String newFileName = fileUid + "." + ext;
String fullPath = FileHelper.saveFile(file, newFileName);
String description = getString(request, "description");
// 保存文件上传记录
createUploadFile(entityClass, fileUid, originFileName, fullPath, ext, description);
return new JsonResult(Status.OK);
}
/**
* 保存上传文件信息
* @param entityClass
* @param fileUid
* @param originFileName
* @param storagePath
* @param fileType
* @param description
* @param <T>
* @throws Exception
*/
protected <T> void createUploadFile(Class<T> entityClass, String fileUid, String originFileName, String storagePath, String fileType, String description) throws Exception{
// 保存文件之后的处理逻辑
int dataCount = extractDataCount(fileUid, storagePath);
UploadFile uploadFile = new UploadFile();
uploadFile.setUuid(fileUid).setFileName(originFileName).setFileType(fileType);
uploadFile.setRelObjType(entityClass.getSimpleName()).setStoragePath(storagePath);
// 初始设置为0批量保存数据后更新
uploadFile.setDataCount(dataCount).setDescription(description);
// 保存文件上传记录
uploadFileService.createEntity(uploadFile);
}
/**
* 保存文件之后的处理逻辑如解析excel
*/
protected int extractDataCount(String fileUuid, String fullPath) throws Exception{
return 0;
}
}

View File

@ -0,0 +1,62 @@
package com.diboot.component.file.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.diboot.core.entity.BaseEntity;
import lombok.Getter;
import lombok.Setter;
import lombok.experimental.Accessors;
import org.hibernate.validator.constraints.Length;
import javax.validation.constraints.NotNull;
/**
* file基础父类
* @author wangyl@dibo.ltd
* @version v2.0
* @date 2019/07/18
*/
@Getter @Setter @Accessors(chain = true)
public class UploadFile extends BaseEntity {
private static final long serialVersionUID = 201L;
// 废弃默认主键
@TableField(exist = false)
private Long id;
// 声明新主键uuid
@TableId(type = IdType.UUID)
private String uuid;
@NotNull(message = "关联对象类不能为空!")
@TableField
private String relObjType = null;
@TableField
@NotNull(message = "关联对象ID不能为空")
private Long relObjId;
@TableField
@NotNull(message = "文件名不能为空!")
@Length(max = 100, message = "文件名长度超出了最大限制!")
private String fileName;
@TableField
@NotNull(message = "文件路径不能为空!")
@Length(max = 200, message = "文件路径长度超出了最大限制!")
private String storagePath;
@TableField
@Length(max = 20, message = "文件类型长度超出了最大限制!")
private String fileType;
/**
* 文件包含记录数
*/
@TableField
private int dataCount = 0;
@TableField
@Length(max = 200, message = "备注长度超出了最大限制!")
private String description;
}

View File

@ -0,0 +1,48 @@
package com.diboot.component.file.excel;
import com.alibaba.fastjson.annotation.JSONField;
import java.io.Serializable;
/***
* excel数据导入导出实体基类
* @auther wangyl@dibo.ltd
* @date 2019-10-9
*/
public class BaseExcelModel implements Serializable {
private static final long serialVersionUID = 6343247548525494223L;
/**
* 验证错误
*/
@JSONField(serialize = false)
private String validateError;
@JSONField(serialize = false)
private int rowIndex;
public int getRowIndex(){
return rowIndex;
}
public String getValidateError(){
return validateError;
}
/**
* 绑定错误
* @param validateError
*/
public void addValidateError(String validateError){
if(this.validateError == null){
this.validateError = validateError;
}
else{
this.validateError += ", " + validateError;
}
}
public void setRowIndex(int rowIndex){
this.rowIndex = rowIndex;
}
}

View File

@ -0,0 +1,50 @@
package com.diboot.component.file.excel.converter;
import com.alibaba.excel.converters.Converter;
import com.alibaba.excel.enums.CellDataTypeEnum;
import com.alibaba.excel.metadata.CellData;
import com.alibaba.excel.metadata.GlobalConfiguration;
import com.alibaba.excel.metadata.property.ExcelContentProperty;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.math.BigDecimal;
/***
* excel解析 Converter
* @auther wangyl@dibo.ltd
* @date 2019-10-9
*/
public class BigDecimalConverter implements Converter<BigDecimal> {
private static final Logger log = LoggerFactory.getLogger(BigDecimalConverter.class);
@Override
public Class supportJavaTypeKey() {
return BigDecimal.class;
}
@Override
public CellDataTypeEnum supportExcelTypeKey() {
return CellDataTypeEnum.NUMBER;
}
@Override
public BigDecimal convertToJavaData(CellData cellData, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) throws Exception {
//当前列
String colName = contentProperty.getHead().getHeadNameList().get(0);
BigDecimal value = null;
try {
value = cellData.getNumberValue();
} catch (Exception e) {
log.warn("["+colName+"]列数据格式有误,请填写正确的浮点型数据", e);
throw new Exception("["+colName+"]列数据格式有误,请填写正确的浮点型数据");
}
return value;
}
@Override
public CellData convertToExcelData(BigDecimal value, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) throws Exception {
return new CellData(value);
}
}

View File

@ -0,0 +1,50 @@
package com.diboot.component.file.excel.converter;
import com.alibaba.excel.converters.Converter;
import com.alibaba.excel.enums.CellDataTypeEnum;
import com.alibaba.excel.metadata.CellData;
import com.alibaba.excel.metadata.GlobalConfiguration;
import com.alibaba.excel.metadata.property.ExcelContentProperty;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/***
* excel解析 Converter
* @auther wangyl@dibo.ltd
* @date 2019-10-9
*/
public class BooleanConverter implements Converter<Boolean> {
private static final Logger log = LoggerFactory.getLogger(BooleanConverter.class);
@Override
public Class supportJavaTypeKey() {
return Boolean.class;
}
@Override
public CellDataTypeEnum supportExcelTypeKey() {
return CellDataTypeEnum.BOOLEAN;
}
@Override
public Boolean convertToJavaData(CellData cellData, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) throws Exception {
Boolean value = null;
String colName = contentProperty.getHead().getHeadNameList().get(0);//当前列
if(!CellDataTypeEnum.BOOLEAN.equals(cellData.getType())){
log.warn("["+colName+"]列数据格式有误,请填写正确的逻辑类型数据");
throw new Exception("["+colName+"]列数据格式有误,请填写正确的逻辑类型数据");
}
try {
value = cellData.getBooleanValue();
} catch (Exception e) {
log.warn("["+colName+"]列数据格式有误,请填写正确的逻辑类型数据", e);
throw new Exception("["+colName+"]列数据格式有误,请填写正确的逻辑类型数据");
}
return value;
}
@Override
public CellData convertToExcelData(Boolean value, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) throws Exception {
return new CellData(value);
}
}

View File

@ -0,0 +1,55 @@
package com.diboot.component.file.excel.converter;
import com.alibaba.excel.converters.Converter;
import com.alibaba.excel.enums.CellDataTypeEnum;
import com.alibaba.excel.metadata.CellData;
import com.alibaba.excel.metadata.GlobalConfiguration;
import com.alibaba.excel.metadata.property.ExcelContentProperty;
import com.alibaba.excel.util.DateUtils;
import org.apache.poi.ss.usermodel.DateUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Date;
/***
* excel解析 Converter
* @auther wangyl@dibo.ltd
* @date 2019-10-9
*/
public class DateConverter implements Converter<Date> {
private static final Logger log = LoggerFactory.getLogger(DateConverter.class);
@Override
public Class supportJavaTypeKey() {
return Date.class;
}
@Override
public CellDataTypeEnum supportExcelTypeKey() {
return CellDataTypeEnum.NUMBER;
}
@Override
public Date convertToJavaData(CellData cellData, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) throws Exception {
Date value = null;
String colName = contentProperty.getHead().getHeadNameList().get(0);//当前列
try {
value = DateUtil.getJavaDate(cellData.getNumberValue().doubleValue(),
globalConfiguration.getUse1904windowing(), null);
} catch (Exception e) {
log.warn("["+colName+"]列数据格式有误,请填写正确的时间类型数据", e);
throw new Exception("["+colName+"]列数据格式有误,请填写正确的时间类型数据");
}
return value;
}
@Override
public CellData convertToExcelData(Date value, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) throws Exception {
if (contentProperty == null || contentProperty.getDateTimeFormatProperty() == null) {
return new CellData(DateUtils.format(value, null));
} else {
return new CellData(DateUtils.format(value, contentProperty.getDateTimeFormatProperty().getFormat()));
}
}
}

View File

@ -0,0 +1,48 @@
package com.diboot.component.file.excel.converter;
import com.alibaba.excel.converters.Converter;
import com.alibaba.excel.enums.CellDataTypeEnum;
import com.alibaba.excel.metadata.CellData;
import com.alibaba.excel.metadata.GlobalConfiguration;
import com.alibaba.excel.metadata.property.ExcelContentProperty;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.math.BigDecimal;
/***
* excel解析 Converter
* @auther wangyl@dibo.ltd
* @date 2019-10-9
*/
public class DoubleConverter implements Converter<Double> {
private static final Logger log = LoggerFactory.getLogger(DoubleConverter.class);
@Override
public Class supportJavaTypeKey() {
return Double.class;
}
@Override
public CellDataTypeEnum supportExcelTypeKey() {
return CellDataTypeEnum.NUMBER;
}
@Override
public Double convertToJavaData(CellData cellData, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) throws Exception {
Double value = null;
String colName = contentProperty.getHead().getHeadNameList().get(0);//当前列
try {
value = cellData.getNumberValue().doubleValue();
} catch (Exception e) {
log.warn("["+colName+"]列数据格式有误,请填写正确的浮点型数据", e);
throw new Exception("["+colName+"]列数据格式有误,请填写正确的浮点型数据");
}
return value;
}
@Override
public CellData convertToExcelData(Double value, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) throws Exception {
return new CellData(BigDecimal.valueOf(value));
}
}

View File

@ -0,0 +1,48 @@
package com.diboot.component.file.excel.converter;
import com.alibaba.excel.converters.Converter;
import com.alibaba.excel.enums.CellDataTypeEnum;
import com.alibaba.excel.metadata.CellData;
import com.alibaba.excel.metadata.GlobalConfiguration;
import com.alibaba.excel.metadata.property.ExcelContentProperty;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/***
* excel解析 Converter
* @auther wangyl@dibo.ltd
* @date 2019-10-9
*/
public class IntegerConverter implements Converter<Integer> {
private static final Logger log = LoggerFactory.getLogger(IntegerConverter.class);
@Override
public Class supportJavaTypeKey() {
return Integer.class;
}
@Override
public CellDataTypeEnum supportExcelTypeKey() {
return CellDataTypeEnum.NUMBER;
}
@Override
public Integer convertToJavaData(CellData cellData, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) throws Exception {
Integer value = null;
String colName = contentProperty.getHead().getHeadNameList().get(0);//当前列
try {
value = cellData.getNumberValue().intValue();
}
catch (Exception e) {
log.warn("["+colName+"]列数据格式有误,请填写正确的整型数据", e);
throw new Exception("["+colName+"]列数据格式有误,请填写正确的整型数据");
}
return value;
}
@Override
public CellData convertToExcelData(Integer value, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) throws Exception {
return new CellData(value.toString());
}
}

View File

@ -0,0 +1,34 @@
package com.diboot.component.file.excel.converter;
import com.alibaba.excel.converters.Converter;
import com.alibaba.excel.enums.CellDataTypeEnum;
import com.alibaba.excel.metadata.CellData;
import com.alibaba.excel.metadata.GlobalConfiguration;
import com.alibaba.excel.metadata.property.ExcelContentProperty;
/***
* excel解析 Converter
* @auther wangyl@dibo.ltd
* @date 2019-10-9
*/
public class StringConverter implements Converter<String> {
@Override
public Class supportJavaTypeKey() {
return String.class;
}
@Override
public CellDataTypeEnum supportExcelTypeKey() {
return CellDataTypeEnum.STRING;
}
@Override
public String convertToJavaData(CellData cellData, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) throws Exception {
return cellData.getStringValue();
}
@Override
public CellData convertToExcelData(String value, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) throws Exception {
return new CellData(value);
}
}

View File

@ -0,0 +1,64 @@
package com.diboot.component.file.excel.listener;
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import lombok.extern.slf4j.Slf4j;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
/***
* excel数据导入导出listener基类
* @auther wangyl@dibo.ltd
* @date 2019-10-9
*/
@Slf4j
public abstract class DynamicHeadExcelListener extends AnalysisEventListener<Map<Integer, String>> {
// 表头
Map<Integer, String> headMap = null;
// 全部数据
private List<Map<Integer, String>> dataList = new ArrayList<>();
@Override
public void invoke(Map<Integer, String> data, AnalysisContext context) {
dataList.add(data);
}
@Override
public void doAfterAllAnalysed(AnalysisContext context) {
saveData(this.headMap, this.dataList);
}
/**
* 表头
* @param headMap
* @param context
*/
@Override
public void invokeHeadMap(Map<Integer, String> headMap, AnalysisContext context) {
this.headMap = headMap;
}
/**
* 返回表头
* @return
*/
public Map<Integer, String> getHeadMap(){
return this.headMap;
}
/**
* 返回结果
* @return
*/
public List<Map<Integer, String>> getDataList(){
return dataList;
}
/**
* 保存数据
*/
protected abstract void saveData(Map<Integer, String> headMap, List<Map<Integer, String>> dataList);
}

View File

@ -0,0 +1,185 @@
package com.diboot.component.file.excel.listener;
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import com.alibaba.excel.exception.ExcelDataConvertException;
import com.diboot.component.file.excel.BaseExcelModel;
import com.diboot.core.exception.BusinessException;
import com.diboot.core.util.BeanUtils;
import com.diboot.core.util.S;
import com.diboot.core.util.V;
import com.diboot.core.vo.Status;
import lombok.extern.slf4j.Slf4j;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
/***
* excel数据导入导出listener基类
* @auther wangyl@dibo.ltd
* @date 2019-10-9
*/
@Slf4j
public abstract class FixedHeadExcelListener<T extends BaseExcelModel> extends AnalysisEventListener<T> {
//解析出的excel表头
protected Map<Integer, String> headMap;
//解析后的数据实体list
protected List<T> dataList = new ArrayList<>();
//错误信息
private List<String> validateErrorMsgs = new ArrayList<>();
//是否预览模式默认否
protected boolean preview = false;
// 导入文件的uuid
protected String uploadFileUuid;
public FixedHeadExcelListener(){
}
public void setPreview(boolean isPreview){
this.preview = isPreview;
}
public void setUploadFileUuid(String uploadFileUuid){
this.uploadFileUuid = uploadFileUuid;
}
/**
* 当前一行数据解析成功后的操作
**/
@Override
public void invoke(T data, AnalysisContext context) {
// 绑定行号
data.setRowIndex(context.readRowHolder().getRowIndex());
dataList.add(data);
}
/**
* 所有数据解析成功后的操作
**/
@Override
public void doAfterAllAnalysed(AnalysisContext context) {
//表头和数据校验
validateHeaderAndDataList();
// 收集校验异常信息
if(V.notEmpty(dataList)){
//自定义数据校验
additionalValidate(dataList);
// 提取校验结果
dataList.stream().forEach(data->{
if(V.notEmpty(data.getValidateError())){
validateErrorMsgs.add(data.getRowIndex() + "行: " + data.getValidateError());
}
});
}
// 有错误 抛出异常
if(V.notEmpty(this.validateErrorMsgs)){
throw new BusinessException(Status.FAIL_VALIDATION, S.join(this.validateErrorMsgs, "; "));
}
// 非预览模式 才保存
if(preview == false){
// 保存数据
saveData(dataList);
}
}
/**
* 在转换异常获取其他异常下会调用本接口
* 抛出异常则停止读取如果这里不抛出异常则 继续读取下一行
**/
@Override
public void onException(Exception exception, AnalysisContext context) throws Exception {
int currentRowNum = context.readRowHolder().getRowIndex();
//数据类型转化异常
if (exception instanceof ExcelDataConvertException) {
log.error("数据转换异常", exception);
validateErrorMsgs.add(currentRowNum+"行: "+exception.getCause().getMessage());
}
else{//其他异常
log.error("出现暂未处理的异常:",exception);
validateErrorMsgs.add("解析异常: "+exception.getMessage());
}
}
/**
* excel表头数据
**/
@Override
public void invokeHeadMap(Map<Integer, String> headMap, AnalysisContext context) {
this.headMap = headMap;
}
/**
* 校验表头, 校验数据实体list
* */
private void validateHeaderAndDataList() {
// 校验数据是否合法
if(V.notEmpty(dataList)){
dataList.stream().forEach(data->{
String errMsg = V.validateBean(data);
if(V.notEmpty(errMsg)){
data.addValidateError(errMsg);
}
});
}
}
/**
* 自定义数据检验方式数据重复性校验等,返回校验日志信息
**/
protected abstract void additionalValidate(List<T> dataList);
/**
* 保存数据
*/
protected abstract void saveData(List<T> dataList);
/**
* 校验错误信息
* @return
*/
public List<String> getErrorMsgs(){
return this.validateErrorMsgs;
}
/**
* 返回表头
* @return
*/
public Map<Integer, String> getHeadMap(){
return this.headMap;
}
/**
* 返回按顺序的string表头
* @return
*/
public List<String> getHeadList(){
Integer[] indexArray = (Integer[]) this.headMap.keySet().toArray();
Arrays.sort(indexArray);
List<String> headList = new ArrayList<>(indexArray.length);
for(Integer idx : indexArray){
headList.add(this.headMap.get(idx));
}
return headList;
}
/**
* 返回数据
* @return
*/
public List<T> getDataList(){
return dataList;
}
/***
* 获取Excel映射的Model类
* @return
*/
public Class<T> getExcelModelClass(){
return BeanUtils.getGenericityClass(this, 0);
}
}

View File

@ -0,0 +1,15 @@
package com.diboot.component.file.mapper;
import com.diboot.component.file.entity.UploadFile;
import com.diboot.core.mapper.BaseCrudMapper;
import org.apache.ibatis.annotations.Mapper;
/**
* 文件相关Mapper
* @author Mazc
* @version 2017/4/18
*/
@Mapper
public interface UploadFileMapper extends BaseCrudMapper<UploadFile> {
}

View File

@ -0,0 +1,291 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!--
Copyright 2009-2013 the original author or authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed joinOn an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<!ELEMENT mapper (cache-ref | cache | resultMap* | parameterMap* | sql* | insert* | update* | delete* | select* )+>
<!ATTLIST mapper
xmlns:fo CDATA #IMPLIED
namespace CDATA #IMPLIED
>
<!ELEMENT cache-ref EMPTY>
<!ATTLIST cache-ref
namespace CDATA #REQUIRED
>
<!ELEMENT cache (property*)>
<!ATTLIST cache
type CDATA #IMPLIED
eviction CDATA #IMPLIED
flushInterval CDATA #IMPLIED
size CDATA #IMPLIED
readOnly CDATA #IMPLIED
blocking CDATA #IMPLIED
>
<!ELEMENT parameterMap (parameter+)?>
<!ATTLIST parameterMap
id CDATA #REQUIRED
type CDATA #REQUIRED
>
<!ELEMENT parameter EMPTY>
<!ATTLIST parameter
property CDATA #REQUIRED
javaType CDATA #IMPLIED
jdbcType CDATA #IMPLIED
mode (IN | OUT | INOUT) #IMPLIED
resultMap CDATA #IMPLIED
scale CDATA #IMPLIED
typeHandler CDATA #IMPLIED
>
<!ELEMENT resultMap (constructor?,id*,result*,association*,collection*, discriminator?)>
<!ATTLIST resultMap
id CDATA #REQUIRED
type CDATA #REQUIRED
extends CDATA #IMPLIED
autoMapping (true|false) #IMPLIED
>
<!ELEMENT constructor (idArg*,arg*)>
<!ELEMENT id EMPTY>
<!ATTLIST id
property CDATA #IMPLIED
javaType CDATA #IMPLIED
column CDATA #IMPLIED
jdbcType CDATA #IMPLIED
typeHandler CDATA #IMPLIED
>
<!ELEMENT result EMPTY>
<!ATTLIST result
property CDATA #IMPLIED
javaType CDATA #IMPLIED
column CDATA #IMPLIED
jdbcType CDATA #IMPLIED
typeHandler CDATA #IMPLIED
>
<!ELEMENT idArg EMPTY>
<!ATTLIST idArg
javaType CDATA #IMPLIED
column CDATA #IMPLIED
jdbcType CDATA #IMPLIED
typeHandler CDATA #IMPLIED
select CDATA #IMPLIED
resultMap CDATA #IMPLIED
>
<!ELEMENT arg EMPTY>
<!ATTLIST arg
javaType CDATA #IMPLIED
column CDATA #IMPLIED
jdbcType CDATA #IMPLIED
typeHandler CDATA #IMPLIED
select CDATA #IMPLIED
resultMap CDATA #IMPLIED
>
<!ELEMENT collection (constructor?,id*,result*,association*,collection*, discriminator?)>
<!ATTLIST collection
property CDATA #REQUIRED
column CDATA #IMPLIED
javaType CDATA #IMPLIED
ofType CDATA #IMPLIED
jdbcType CDATA #IMPLIED
select CDATA #IMPLIED
resultMap CDATA #IMPLIED
typeHandler CDATA #IMPLIED
notNullColumn CDATA #IMPLIED
columnPrefix CDATA #IMPLIED
resultSet CDATA #IMPLIED
foreignColumn CDATA #IMPLIED
autoMapping (true|false) #IMPLIED
fetchType (lazy|eager) #IMPLIED
>
<!ELEMENT association (constructor?,id*,result*,association*,collection*, discriminator?)>
<!ATTLIST association
property CDATA #REQUIRED
column CDATA #IMPLIED
javaType CDATA #IMPLIED
jdbcType CDATA #IMPLIED
select CDATA #IMPLIED
resultMap CDATA #IMPLIED
typeHandler CDATA #IMPLIED
notNullColumn CDATA #IMPLIED
columnPrefix CDATA #IMPLIED
resultSet CDATA #IMPLIED
foreignColumn CDATA #IMPLIED
autoMapping (true|false) #IMPLIED
fetchType (lazy|eager) #IMPLIED
>
<!ELEMENT discriminator (case+)>
<!ATTLIST discriminator
column CDATA #IMPLIED
javaType CDATA #REQUIRED
jdbcType CDATA #IMPLIED
typeHandler CDATA #IMPLIED
>
<!ELEMENT case (constructor?,id*,result*,association*,collection*, discriminator?)>
<!ATTLIST case
value CDATA #REQUIRED
resultMap CDATA #IMPLIED
resultType CDATA #IMPLIED
>
<!ELEMENT property EMPTY>
<!ATTLIST property
name CDATA #REQUIRED
value CDATA #REQUIRED
>
<!ELEMENT typeAlias EMPTY>
<!ATTLIST typeAlias
alias CDATA #REQUIRED
type CDATA #REQUIRED
>
<!ELEMENT select (#PCDATA | include | trim | where | set | foreach | choose | if | bind)*>
<!ATTLIST select
id CDATA #REQUIRED
parameterMap CDATA #IMPLIED
parameterType CDATA #IMPLIED
resultMap CDATA #IMPLIED
resultType CDATA #IMPLIED
resultSetType (FORWARD_ONLY | SCROLL_INSENSITIVE | SCROLL_SENSITIVE) #IMPLIED
statementType (STATEMENT|PREPARED|CALLABLE) #IMPLIED
fetchSize CDATA #IMPLIED
timeout CDATA #IMPLIED
flushCache (true|false) #IMPLIED
useCache (true|false) #IMPLIED
databaseId CDATA #IMPLIED
lang CDATA #IMPLIED
resultOrdered (true|false) #IMPLIED
resultSets CDATA #IMPLIED
>
<!ELEMENT insert (#PCDATA | selectKey | include | trim | where | set | foreach | choose | if | bind)*>
<!ATTLIST insert
id CDATA #REQUIRED
parameterMap CDATA #IMPLIED
parameterType CDATA #IMPLIED
timeout CDATA #IMPLIED
flushCache (true|false) #IMPLIED
statementType (STATEMENT|PREPARED|CALLABLE) #IMPLIED
keyProperty CDATA #IMPLIED
useGeneratedKeys (true|false) #IMPLIED
keyColumn CDATA #IMPLIED
databaseId CDATA #IMPLIED
lang CDATA #IMPLIED
>
<!ELEMENT selectKey (#PCDATA | include | trim | where | set | foreach | choose | if | bind)*>
<!ATTLIST selectKey
resultType CDATA #IMPLIED
statementType (STATEMENT|PREPARED|CALLABLE) #IMPLIED
keyProperty CDATA #IMPLIED
keyColumn CDATA #IMPLIED
order (BEFORE|AFTER) #IMPLIED
databaseId CDATA #IMPLIED
>
<!ELEMENT update (#PCDATA | selectKey | include | trim | where | set | foreach | choose | if | bind)*>
<!ATTLIST update
id CDATA #REQUIRED
parameterMap CDATA #IMPLIED
parameterType CDATA #IMPLIED
timeout CDATA #IMPLIED
flushCache (true|false) #IMPLIED
statementType (STATEMENT|PREPARED|CALLABLE) #IMPLIED
keyProperty CDATA #IMPLIED
useGeneratedKeys (true|false) #IMPLIED
keyColumn CDATA #IMPLIED
databaseId CDATA #IMPLIED
lang CDATA #IMPLIED
>
<!ELEMENT delete (#PCDATA | include | trim | where | set | foreach | choose | if | bind)*>
<!ATTLIST delete
id CDATA #REQUIRED
parameterMap CDATA #IMPLIED
parameterType CDATA #IMPLIED
timeout CDATA #IMPLIED
flushCache (true|false) #IMPLIED
statementType (STATEMENT|PREPARED|CALLABLE) #IMPLIED
databaseId CDATA #IMPLIED
lang CDATA #IMPLIED
>
<!-- Dynamic -->
<!ELEMENT include (property+)?>
<!ATTLIST include
refid CDATA #REQUIRED
>
<!ELEMENT bind EMPTY>
<!ATTLIST bind
name CDATA #REQUIRED
value CDATA #REQUIRED
>
<!ELEMENT sql (#PCDATA | include | trim | where | set | foreach | choose | if | bind)*>
<!ATTLIST sql
id CDATA #REQUIRED
lang CDATA #IMPLIED
databaseId CDATA #IMPLIED
>
<!ELEMENT trim (#PCDATA | include | trim | where | set | foreach | choose | if | bind)*>
<!ATTLIST trim
prefix CDATA #IMPLIED
prefixOverrides CDATA #IMPLIED
suffix CDATA #IMPLIED
suffixOverrides CDATA #IMPLIED
>
<!ELEMENT where (#PCDATA | include | trim | where | set | foreach | choose | if | bind)*>
<!ELEMENT set (#PCDATA | include | trim | where | set | foreach | choose | if | bind)*>
<!ELEMENT foreach (#PCDATA | include | trim | where | set | foreach | choose | if | bind)*>
<!ATTLIST foreach
collection CDATA #REQUIRED
item CDATA #IMPLIED
index CDATA #IMPLIED
open CDATA #IMPLIED
close CDATA #IMPLIED
separator CDATA #IMPLIED
>
<!ELEMENT choose (when* , otherwise?)>
<!ELEMENT when (#PCDATA | include | trim | where | set | foreach | choose | if | bind)*>
<!ATTLIST when
test CDATA #REQUIRED
>
<!ELEMENT otherwise (#PCDATA | include | trim | where | set | foreach | choose | if | bind)*>
<!ELEMENT if (#PCDATA | include | trim | where | set | foreach | choose | if | bind)*>
<!ATTLIST if
test CDATA #REQUIRED
>

View File

@ -0,0 +1,23 @@
package com.diboot.component.file.service;
import com.diboot.component.file.entity.UploadFile;
import com.diboot.core.service.BaseService;
import java.util.List;
/**
* 基础文件Service
* @author Lishuaifei@dibo.ltd
* @date 2019-07-18
*/
public interface UploadFileService extends BaseService<UploadFile> {
/**
* 获取指定对象记录关联的上传文件列表
* @param relObjClass
* @param relObjId
* @return
*/
List<UploadFile> getUploadedFiles(String relObjClass, Long relObjId);
}

View File

@ -0,0 +1,30 @@
package com.diboot.component.file.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.diboot.component.file.entity.UploadFile;
import com.diboot.component.file.mapper.UploadFileMapper;
import com.diboot.component.file.service.UploadFileService;
import com.diboot.core.service.impl.BaseServiceImpl;
import org.springframework.stereotype.Service;
import java.util.List;
/**
* 文件下载实现类
* @author Lishuaifei@dibo.ltd
* @date 2019-07-18
*/
@Service
public class UploadFileServiceImpl extends BaseServiceImpl<UploadFileMapper, UploadFile> implements UploadFileService {
@Override
public List<UploadFile> getUploadedFiles(String relObjClass, Long relObjId) {
LambdaQueryWrapper<UploadFile> queryWrapper = new QueryWrapper<UploadFile>().lambda()
.eq(UploadFile::getRelObjType, relObjClass)
.eq(UploadFile::getRelObjId, relObjId)
.orderByAsc(UploadFile::getCreateTime);
return getEntityList(queryWrapper);
}
}

View File

@ -0,0 +1,38 @@
package com.diboot.component.file.starter;
import lombok.extern.slf4j.Slf4j;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
@Slf4j
@Configuration
@EnableConfigurationProperties(FileProperties.class)
@ComponentScan(basePackages = {"com.diboot.component.file"})
@MapperScan(basePackages = {"com.diboot.component.file.mapper"})
public class FileAutoConfiguration {
@Autowired
FileProperties fileProperties;
@Autowired
Environment environment;
@Bean
@ConditionalOnMissingBean(FilePluginManager.class)
public FilePluginManager filePluginManager(){
// 初始化SCHEMA
SqlHandler.init(environment);
FilePluginManager pluginManager = new FilePluginManager() {};
// 检查数据库字典是否已存在
if(fileProperties.isInitSql() && SqlHandler.checkIsFileTableExists() == false){
SqlHandler.initBootstrapSql(pluginManager.getClass(), environment, "file");
}
return pluginManager;
}
}

View File

@ -0,0 +1,7 @@
package com.diboot.component.file.starter;
import com.diboot.core.plugin.PluginManager;
public class FilePluginManager implements PluginManager {
}

View File

@ -0,0 +1,17 @@
package com.diboot.component.file.starter;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
/**
* @author diboot
*/
@Data
@ConfigurationProperties(prefix = "diboot.component.file")
public class FileProperties {
/**
* 是否初始化默认true自动安装SQL
*/
private boolean initSql = true;
}

View File

@ -0,0 +1,28 @@
package com.diboot.component.file.starter;
import lombok.extern.slf4j.Slf4j;
/**
* SQL处理类
* @author Yangzhao
* @version v2.0
* @date 2019/08/01
*/
@Slf4j
public class SqlHandler extends com.diboot.core.starter.SqlHandler {
// 文件SQL
private static final String FILE_SQL = "SELECT id FROM ${SCHEMA}.file WHERE id=0";
// 列定义SQL
private static final String EXCEL_COLUMN_SQL = "SELECT id FROM ${SCHEMA}.excel_column WHERE id=0";
// Excel导入记录SQL
private static final String EXCEL_IMPORT_RECORD_SQL = "SELECT id FROM ${SCHEMA}.excel_import_record WHERE id=0";
/**
* 检查file表是否已存在
* @return
*/
public static boolean checkIsFileTableExists(){
return checkIsTableExists(FILE_SQL);
}
}

View File

@ -0,0 +1,115 @@
package com.diboot.component.file.util;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.write.style.column.LongestMatchColumnWidthStyleStrategy;
import com.diboot.component.file.excel.BaseExcelModel;
import com.diboot.component.file.excel.listener.DynamicHeadExcelListener;
import com.diboot.component.file.excel.listener.FixedHeadExcelListener;
import com.diboot.core.exception.BusinessException;
import com.diboot.core.util.BeanUtils;
import com.diboot.core.util.V;
import com.diboot.core.vo.Status;
import lombok.extern.slf4j.Slf4j;
import java.io.File;
import java.util.Collections;
import java.util.List;
/***
* excel数据导入导出工具类
* @auther wangyl@dibo.ltd
* @date 2019-10-9
*/
@Slf4j
public class ExcelHelper {
/**
* 预览读取excel文件数据,不保存到数据库
* @param filePath
* @param listener
* @return
*/
public static <T extends BaseExcelModel> List<T> previewRead(String filePath, FixedHeadExcelListener listener) throws Exception{
File excel = getExcelFile(filePath);
listener.setPreview(true);
Class<T> headClazz = BeanUtils.getGenericityClass(listener, 0);
EasyExcel.read(excel).head(headClazz).registerReadListener(listener).sheet().doRead();
return listener.getDataList();
}
/**
* 简单读取excel文件数据并保存到数据库
* @param filePath
* @param listener
* @return
*/
public static <T extends BaseExcelModel> boolean readAndSave(String filePath, FixedHeadExcelListener listener) throws Exception{
File excel = getExcelFile(filePath);
Class<T> headClazz = BeanUtils.getGenericityClass(listener, 0);
EasyExcel.read(excel).registerReadListener(listener).head(headClazz).sheet().doRead();
return true;
}
/**
* 读取非确定/动态表头的excel文件数据
* @param filePath
* @return
*/
public static boolean readDynamicHeadExcel(String filePath, DynamicHeadExcelListener listener){
File excel = getExcelFile(filePath);
EasyExcel.read(excel).registerReadListener(listener).sheet().doRead();
return true;
}
/**
* 简单将数据写入excel文件,列宽自适应数据长度
* @param filePath
* @param sheetName
* @param dataList
* @return
*/
public static boolean writeData(String filePath, String sheetName, List<List<String>> dataList) throws Exception{
try {
EasyExcel.write(filePath).registerWriteHandler(new LongestMatchColumnWidthStyleStrategy()).sheet(sheetName).doWrite(dataList);
return true;
}
catch (Exception e) {
log.error("数据写入excel文件失败",e);
return false;
}
}
/**
* 简单将数据写入excel文件,列宽自适应数据长度
* @param filePath
* @param sheetName
* @param dataList
* @param <T>
* @return
*/
public static <T extends BaseExcelModel> boolean writeDate(String filePath, String sheetName, List<T> dataList) throws Exception{
try {
if(V.isEmpty(dataList)){
return writeData(filePath, sheetName, Collections.emptyList());
}
Class<T> tClass = (Class<T>) dataList.get(0).getClass();
EasyExcel.write(filePath, tClass).registerWriteHandler(new LongestMatchColumnWidthStyleStrategy()).sheet(sheetName).doWrite(dataList);
return true;
}
catch (Exception e) {
log.error("数据写入excel文件失败",e);
return false;
}
}
private static File getExcelFile(String filePath){
File file = new File(filePath);
if(!file.exists()){
log.error("找不到指定文件,路径:"+filePath);
throw new BusinessException(Status.FAIL_EXCEPTION, "找不到指定文件,导入excel失败");
}
return file;
}
}

View File

@ -0,0 +1,212 @@
package com.diboot.component.file.util;
import com.diboot.component.file.config.Cons;
import com.diboot.core.util.D;
import com.diboot.core.util.PropertiesUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.web.multipart.MultipartFile;
import java.io.File;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;
/***
* 文件操作辅助类
* @author Mazc
*/
@Slf4j
public class FileHelper{
/**
* file验证
*/
public static final List<String> DANGER_FILE_SUFFIX = Arrays.asList("exe", "bat", "bin", "dll", "sh");
/**
* excel格式
*/
private static final List<String> EXCEL_SUFFIX = Arrays.asList("xls", "xlsx", "xlsm");
/**
* 文件存储路径参数名
*/
public static final String FILE_STORAGE_DIRECTORY = "files.storage.directory";
/**
* 文件存储路径
*/
private static final String PATH_FILE = "/upload/file";
public static final String POINT = ".";
public static final String HTTP = "http";
public static final String QUESTION_MARK = "?";
/**
* 文件和图片的后台存储路径
*/
private static String fileStorageDirectory = null;
/***
* 是否为合法的文件类型
* @param ext
* @return
*/
public static boolean isValidFileExt(String ext){
return !DANGER_FILE_SUFFIX.contains(ext.toLowerCase());
}
/**
* 是否是Excel文件
* @param fileName
* @return
*/
public static boolean isExcel(String fileName){
String ext = FileHelper.getFileExtByName(fileName);
return EXCEL_SUFFIX.contains(ext.toLowerCase());
}
/***
* 获取系统临时目录
* @return
*/
public static String getSystemTempDir(){
return System.getProperty("java.io.tmpdir");
}
/***
* 将文件保存到系统临时目录
* @param file
* @param fileName
* @return
*/
public static String saveFile2TempDir(MultipartFile file, String fileName){
String fullPath = getSystemTempDir() + fileName;
try {
FileUtils.writeByteArrayToFile(new File(fullPath), file.getBytes());
return fileName;
}
catch (IOException e1) {
log.error("保存原图片失败(image=" + fileName + "): ", e1);
return null;
}
}
/***
* 上传文件
* @param file 上传文件
* @param fileName 文件名
* @return
*/
public static String saveFile(MultipartFile file, String fileName) {
// 生成文件路径
String fullPath = getFullPath(fileName);
try {
// 创建文件夹
makeDirectory(fullPath);
FileUtils.writeByteArrayToFile(new File(fullPath), file.getBytes());
if(log.isDebugEnabled()){
log.debug("保存文件成功!路径为: " + fullPath);
}
return fullPath;
}
catch (IOException e1) {
log.error("保存文件失败(file=" + fullPath + "): ", e1);
return null;
}
}
/***
* 根据名称取得后缀
* @param fileName
* @return
*/
public static String getFileExtByName(String fileName){
if(fileName.startsWith(HTTP) && fileName.contains(Cons.FILE_PATH_SEPARATOR)){
fileName = getFileName(fileName);
}
if(fileName.lastIndexOf(POINT) > 0){
return fileName.substring(fileName.lastIndexOf(POINT)+1).toLowerCase();
}
log.warn("检测到没有后缀的文件名:" + fileName);
return "";
}
/***
* 获取文件的相对路径
* @param fileName 仅文件名不含相对路径
* @return
*/
public static String getRelativePath(String fileName) {
StringBuilder sb = new StringBuilder();
sb.append(PATH_FILE).append("/").append(D.getYearMonth()).append("/").append(fileName);
return sb.toString();
}
/***
* 获取文件的完整存储路径
* @param fileName 仅文件名不含相对路径
* @return
*/
public static String getFullPath(String fileName) {
String relativePath = getRelativePath(fileName);
return getFileStorageDirectory() + relativePath;
}
/**
* 根据文件URL解析出其文件名
* @param fileUrl
* @return
*/
public static String getFileName(String fileUrl){
String temp = StringUtils.substring(fileUrl, fileUrl.lastIndexOf(Cons.FILE_PATH_SEPARATOR)+1);
if(StringUtils.contains(fileUrl, QUESTION_MARK)){
temp = StringUtils.substring(temp, 0, temp.lastIndexOf(QUESTION_MARK));
}
return temp;
}
/**
* 文件的存储路径
* @return
*/
public static String getFileStorageDirectory(){
if(fileStorageDirectory == null){
fileStorageDirectory = PropertiesUtils.get(FILE_STORAGE_DIRECTORY);
}
return fileStorageDirectory;
}
/***
* 创建文件夹
* @param dirPath
* @return
*/
public static boolean makeDirectory(String dirPath){
String directory = StringUtils.substringBeforeLast(dirPath, Cons.FILE_PATH_SEPARATOR);
File dir = new File(directory);
if(!dir.exists()){
try {
FileUtils.forceMkdir(dir);
return true;
}
catch (IOException e) {
log.error("创建文件夹失败", e);
return false;
}
}
return false;
}
/****
* 删除文件
* @param fileStoragePath
*/
public static void deleteFile(String fileStoragePath) {
File file = new File(fileStoragePath);
if(file.exists()){
file.delete();
}
}
}

View File

@ -0,0 +1,259 @@
package com.diboot.component.file.util;
import com.diboot.core.exception.BusinessException;
import com.diboot.core.util.S;
import com.diboot.core.util.V;
import com.diboot.core.vo.Status;
import lombok.extern.slf4j.Slf4j;
import okhttp3.*;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
import org.apache.commons.io.FileUtils;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* HTTP请求相关工具类
* @author mazc@dibo.ltd
* @version v2.0
* @date 2020/02/18
*/
@Slf4j
public class HttpHelper {
/***
* 默认contextType
*/
private static final String DEFAULT_CONTEXT_TYPE = "application/octet-stream";
/***
* 文件扩展名-ContentType的对应关系
*/
private static Map<String, String> EXT_CONTENT_TYPE_MAP = new HashMap(){{
put("xls", "application/x-msdownload");
put("xlsx", "application/x-msdownload");
put("doc", "application/x-msdownload");
put("docx", "application/x-msdownload");
put("dot", "application/x-msdownload");
put("ppt", "application/x-msdownload");
put("pptx", "application/x-msdownload");
put("pdf", "application/pdf");
put("avi", "video/avi");
put("bmp", "application/x-bmp");
put("css", "text/css");
put("dll", "application/x-msdownload");
put("dtd", "text/xml");
put("exe", "application/x-msdownload");
put("gif", "image/gif");
put("htm", "text/html");
put("html", "text/html");
put("ico", "image/x-icon");
put("jpeg", "image/jpeg");
put("jpg", "image/jpeg");
put("js", "application/x-javascript");
put("mp3", "audio/mp3");
put("mp4", "video/mpeg4");
put("png", "image/png");
put("svg", "text/xml");
put("swf", "application/x-shockwave-flash");
put("tif", "image/tiff");
put("tiff", "image/tiff");
put("tld", "text/xml");
put("torrent", "application/x-bittorrent");
put("tsd", "text/xml");
put("txt", "text/plain");
put("wav", "audio/wav");
put("wma", "audio/x-ms-wma");
put("wmf", "application/x-wmf");
put("wsdl", "text/xml");
put("xhtml", "text/html");
put("xml", "text/xml");
put("xsd", "text/xml");
put("xsl", "text/xml");
put("xslt", "text/xml");
put("apk", "application/vnd.android.package-archive");
}};
/**
* 调用Http Get请求
* @param url
* @return
*/
public static String callHttpGet(String url){
return callHttpGet(url, null);
}
/**
* 调用Http Get请求
* @param url
* @return
*/
public static String callHttpGet(String url, Map<String, String> headerMap){
OkHttpClient okHttpClient = new OkHttpClient();
Request.Builder builder = new Request.Builder().url(url);
if(V.notEmpty(headerMap)){
for(Map.Entry<String, String> entry : headerMap.entrySet()){
builder.addHeader(entry.getKey(), entry.getValue());
}
}
Call call = okHttpClient.newCall(builder.build());
return executeCall(call, url);
}
/**
* 发送HTTP Post请求
* @param url
* @return
*/
public static String callHttpPost(String url, Map<String, String> formBody){
OkHttpClient okHttpClient = new OkHttpClient();
FormBody.Builder bodyBuilder = new FormBody.Builder();
if(V.notEmpty(formBody)){
for(Map.Entry<String, String> entry : formBody.entrySet()){
bodyBuilder.add(entry.getKey(), entry.getValue());
}
}
Request request = new Request.Builder().url(url)
.post(bodyBuilder.build())
.build();
Call call = okHttpClient.newCall(request);
return executeCall(call, url);
}
/**
* 执行调用 返回结果
* @param call
* @param url
* @return
*/
private static String executeCall(Call call, String url){
try {
Response response = call.execute();
// 判断状态码
if(response.code() >= 400){
log.warn("请求调用异常 : " + url);
return null;
}
return response.body().string();
} catch (IOException e) {
log.warn("请求调用解析异常 : " + url, e);
return null;
}
}
/**
* 根据文件路径下载服务器本地文件
* @param localFilePath 本地文件路径
* @param exportFileName 导出文件的文件名
* @param response
* @throws Exception
*/
public static void downloadLocalFile(String localFilePath, String exportFileName, HttpServletResponse response) throws Exception{
BufferedInputStream bis = null;
BufferedOutputStream bos = null;
try{
String fileName = new String(exportFileName.getBytes("utf-8"), "ISO8859-1");
long fileLength = new File(localFilePath).length();
response.setContentType(getContextType(fileName));
response.setHeader("Content-disposition", "attachment; filename="+ fileName);
response.setHeader("Content-Length", String.valueOf(fileLength));
bis = new BufferedInputStream(new FileInputStream(localFilePath));
bos = new BufferedOutputStream(response.getOutputStream());
byte[] buff = new byte[2048];
int bytesRead;
while(-1 != (bytesRead = bis.read(buff, 0, buff.length))) {
bos.write(buff, 0, bytesRead);
}
}
catch (Exception e) {
log.error("下载导出文件失败:"+localFilePath, e);
}
finally {
if (bis != null) {
bis.close();
}
if (bos != null) {
bos.close();
}
}
}
/****
* HTTP下载文件
* @param fileUrl
* @param targetFilePath
* @return
*/
public static boolean downloadHttpFile(String fileUrl, String targetFilePath) {
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
.url(fileUrl)
.build();
client.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
e.printStackTrace();
}
@Override
public void onResponse(Call call, Response response) throws IOException {
if (response.isSuccessful()){
FileUtils.copyInputStreamToFile(response.body().byteStream(), new File(targetFilePath));
}
}
});
return true;
}
/**
* 根据文件类型获取ContentType
* @param fileName
* @return
*/
public static String getContextType(String fileName){
String ext = S.substringAfterLast(fileName, ".");
String contentType = EXT_CONTENT_TYPE_MAP.get(ext);
if(contentType == null){
contentType = DEFAULT_CONTEXT_TYPE;
}
return contentType + ";charset=utf-8";
}
/***
* 获取请求中的多个文件数据
* @param request
* @param fileInputName
* @return
*/
public static List<MultipartFile> getFilesFromRequest(HttpServletRequest request, String fileInputName){
// 获取附件文件名
if(fileInputName == null){
throw new BusinessException(Status.FAIL_VALIDATION, "未指定文件名!");
}
// 解析上传文件
boolean isMultipart = ServletFileUpload.isMultipartContent(request);
if(!isMultipart){
throw new BusinessException(Status.FAIL_VALIDATION, "无有效的上传文件!");
}
// 解析上传文件
List<MultipartFile> files = ((MultipartHttpServletRequest)request).getFiles(fileInputName);
return files;
}
/***
* 获取请求中的单个文件数据
* @param request
* @param fileInputName
* @return
*/
public static MultipartFile getFileFromRequest(HttpServletRequest request, String fileInputName){
// 解析上传文件
List<MultipartFile> files = getFilesFromRequest(request, fileInputName);
return V.notEmpty(files)? files.get(0) : null;
}
}

View File

@ -0,0 +1,192 @@
package com.diboot.component.file.util;
import com.diboot.component.file.config.Cons;
import com.diboot.core.util.D;
import com.diboot.core.util.S;
import lombok.extern.slf4j.Slf4j;
import net.coobird.thumbnailator.Thumbnails;
import net.coobird.thumbnailator.geometry.Positions;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.web.multipart.MultipartFile;
import javax.imageio.ImageIO;
import java.io.File;
import java.io.IOException;
import java.util.Base64;
/***
* 图片操作辅助类
* @author Mazc
*/
@Slf4j
public class ImageHelper {
private static final String DATA_IMAGE_FLAG = "data:image/";
private static final String BASE_64_FLAG = "base64,";
private static final String PATH_IMG = "/upload/img";
/**
* image验证
*/
public static final String VALID_IMAGE_SUFFIX = "|png|jpg|jpeg|gif|bmp|";
/**
* 是否是图片类型
* @param ext
* @return
*/
public static boolean isImage(String ext){
return VALID_IMAGE_SUFFIX.contains("|"+ext.toLowerCase()+"|");
}
/**
* 保存图片
* @param file 上传文件
* @param imgName 图片名称
*/
public static String saveImage(MultipartFile file, String imgName){
// 生成图片路径
String accessPath = getImageStoragePath(imgName);
String fullPath = FileHelper.getFileStorageDirectory() + accessPath;
try {
// 创建文件夹
FileHelper.makeDirectory(fullPath);
FileUtils.writeByteArrayToFile(new File(fullPath), file.getBytes());
if(log.isDebugEnabled()){
log.debug("保存图片成功!路径为: " + accessPath);
}
return accessPath;
}
catch (IOException e1) {
log.error("保存原图片失败(image=" + accessPath + "): ", e1);
return null;
}
}
/**
* 保存图片
* @param file 上传文件
*/
public static String saveImage(MultipartFile file){
String fileName = file.getOriginalFilename();
String ext = fileName.substring(fileName.lastIndexOf(".")+1);
String newFileName = S.newUuid() + "." + ext;
return saveImage(file, newFileName);
}
/**
* 保存图片
* @param file 上传文件
* @param imgName 图片名称
* @param reserve 是否保留原图片
* @return
*/
public static String saveImage(File file, String imgName, boolean reserve){
// 生成图片路径
String accessPath = getImageStoragePath(imgName);
String fullPath = FileHelper.getFileStorageDirectory() + accessPath;
try{
// 创建文件夹
FileHelper.makeDirectory(fullPath);
FileUtils.copyFile(new File(fullPath), file);
if(log.isDebugEnabled()){
log.debug("保存图片成功!路径为: " + accessPath);
}
// 如果原文件与目标文件不相等且不保留原文件则删除原文件
if (!reserve && !StringUtils.equals(file.getAbsolutePath(), fullPath)){
FileUtils.forceDelete(file);
}
return accessPath;
} catch (Exception e){
log.error("保存原图片失败(image=" + accessPath + "): ", e);
return null;
}
}
/**
* 得到图片存储的全路径
* @param fileName
* @return
*/
public static String getImageStoragePath(String fileName){
StringBuilder sb = new StringBuilder();
sb.append(PATH_IMG).append(Cons.FILE_PATH_SEPARATOR).append(D.getYearMonth()).append(Cons.FILE_PATH_SEPARATOR).append(fileName);
return sb.toString();
}
/**
* 压缩图片
* @param imgUrl
* @return
*/
public static String generateThumbnail(String imgUrl, int width, int height){
String file = imgUrl.substring(imgUrl.indexOf("/img/"));
String imageFileDirectory = FileHelper.getFileStorageDirectory() + file;
try {
// 压缩图片
String targetFile = imgUrl.replace(".", "_tn.");
Thumbnails.of(imageFileDirectory).size(width, height).outputQuality(0.7f).toFile(FileHelper.getFileStorageDirectory() + targetFile);
return targetFile;
}
catch (IOException e1) {
log.error("压缩图片异常(image=" + imageFileDirectory + "): ", e1);
}
return imgUrl;
}
/**
* 将Base64转换为图片
* @param base64Str
* @param fullFilePath
* @return
* @throws IOException
*/
private static boolean convertBase64ToImage(String base64Str, String fullFilePath){
if(base64Str == null){
return false;
}
if(base64Str.contains(BASE_64_FLAG)){
//int suffixStart = StringUtils.indexOf(base64Str, DATA_IMAGE_FLAG)+ DATA_IMAGE_FLAG.length();
//String suffix = StringUtils.substring(base64Str, suffixStart, StringUtils.indexOf(base64Str, ";", suffixStart));
base64Str = base64Str.substring(base64Str.indexOf(BASE_64_FLAG)+ BASE_64_FLAG.length());
}
try{
byte[] data = Base64.getDecoder().decode(base64Str);
File file = new File(fullFilePath);
FileUtils.writeByteArrayToFile(file, data);
data = null;
return true;
}
catch(Exception e){
log.warn("base", e);
return false;
}
}
/**
* 生成缩略图
* @return
* @throws Exception
*/
public static String generateThumbnail(String sourcePath, String targetPath, int width, int height) throws Exception{
// 创建文件
File file = new File(sourcePath);
if(!file.exists()){
boolean result = file.mkdir();
if(!result){
log.warn("创建文件夹 {} 失败", sourcePath);
}
}
// 生成缩略图
Thumbnails.of(sourcePath).size(width, height).toFile(targetPath);
return targetPath;
}
/**
* 给图片添加水印
* @param filePath
*/
private static void addWatermark(String filePath, String watermark) throws Exception{
Thumbnails.of(filePath).watermark(Positions.BOTTOM_RIGHT, ImageIO.read(new File(watermark)), 0.8f).toFile(filePath);
}
}

View File

@ -0,0 +1,146 @@
package com.diboot.component.file.util;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.util.zip.CRC32;
import java.util.zip.CheckedOutputStream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
/***
* 文件压缩操作辅助类
* @author Mazc
*/
@Slf4j
public class ZipHelper {
/**
* 递归压缩文件
* @param srcRootDir
* @param file
* @param zos
* @return
* @throws Exception
*/
private static void zipFile(String srcRootDir, File file, ZipOutputStream zos, String... matchKeyword) throws Exception {
if (file == null) {
log.error("[Zip]file对象为空,压缩失败!");
return;
}
//如果是文件则直接压缩该文件
if (file.isFile()) {
int count, bufferLen = 1024;
byte[] data = new byte[bufferLen];
String fileName = file.getName();
//包含在指定文件名范围内的文件进行压缩
if (matchKeyword == null || matchKeyword.length == 0 || fileName.indexOf(matchKeyword[0]) >= 0) {
//获取文件相对于压缩文件夹根目录的子路径
String subPath = file.getAbsolutePath();
subPath = subPath.replace("\\", "/"); //解决文件路径分割符Unix和Windows不兼容的问题
if (subPath.indexOf(srcRootDir) != -1) {
subPath = subPath.substring(srcRootDir.length() + File.separator.length());
}
ZipEntry entry = new ZipEntry(subPath);
zos.putNextEntry(entry);
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(file));
while ((count = bis.read(data, 0, bufferLen)) != -1) {
zos.write(data, 0, count);
}
log.info("[Zip]压缩成功:" + file.getName());
bis.close();
zos.closeEntry();
}
}
else {
//压缩目录中的文件或子目录
File[] childFileList = file.listFiles();
String filePath = "";
if(childFileList != null) {
for (int n=0; n<childFileList.length; n++)
{
if (!childFileList[n].isFile()) {
filePath = childFileList[n].getPath();
//不在指定目录名范围内的目录不进行压缩
if (matchKeyword != null && matchKeyword.length > 0 && filePath.indexOf(matchKeyword[0]) == -1) {
continue;
}
}
childFileList[n].getAbsolutePath().indexOf(file.getAbsolutePath());
zipFile(srcRootDir, childFileList[n], zos, matchKeyword);
}
}
}
}
/**
* 对文件或文件目录进行压缩
* @param srcPath 要压缩的源文件路径如果压缩一个文件则为该文件的全路径如果压缩一个目录则为该目录的顶层目录路径
* @param zipPath 压缩文件保存的路径注意zipPath不能是srcPath路径下的子文件夹
* @param zipFileName 压缩文件名
* @param excludeKeyword 需要剔除
* @throws Exception
*/
public static boolean zip(String srcPath, String zipPath, String zipFileName, String... excludeKeyword) throws Exception {
if (StringUtils.isEmpty(srcPath)){
log.error("[Zip]源文件路径为空,压缩失败!");
return false;
}
if (StringUtils.isEmpty(zipPath)){
log.error("[Zip]压缩文件保存的路径为空,压缩失败!");
return false;
}
if (StringUtils.isEmpty(zipFileName)){
log.error("[Zip]压缩文件名为空,压缩失败!");
return false;
}
CheckedOutputStream cos = null;
ZipOutputStream zos = null;
File srcFile = new File(srcPath);
if (srcFile.exists()) {
//判断压缩文件保存的路径是否为源文件路径的子文件夹如果是则终止压缩
if (srcFile.isDirectory() && zipPath.indexOf(srcPath)!=-1) {
log.error("[Zip]压缩文件保存的路径是源文件的字文件夹,压缩失败!");
return false;
}
//判断压缩文件保存的路径是否存在如果不存在则创建目录
File zipDir = new File(zipPath);
if (!zipDir.exists() || !zipDir.isDirectory()) {
zipDir.mkdirs();
}
//创建压缩文件保存的文件对象
String zipFilePath = zipPath + "/" + zipFileName;
File zipFile = new File(zipFilePath);
if (zipFile.exists()){
//删除已存在的目标文件
zipFile.delete();
}
cos = new CheckedOutputStream(new FileOutputStream(zipFile), new CRC32());
zos = new ZipOutputStream(cos);
//如果只是压缩一个文件则需要截取该文件的父目录
String srcRootDir = srcPath;
if (srcFile.isFile()){
int index = srcPath.lastIndexOf("/");
if (index != -1){
srcRootDir = srcPath.substring(0, index);
}
}
//调用递归压缩方法进行目录或文件压缩
zipFile(srcRootDir, srcFile, zos, excludeKeyword);
zos.flush();
if(zos != null){
zos.close();
}
return true;
}
else {
log.error("[Zip]当前源目录不存在,压缩失败!" + srcPath);
return false;
}
}
}

View File

@ -0,0 +1 @@
org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.diboot.component.file.starter.FileAutoConfiguration

View File

@ -0,0 +1,15 @@
-- 上传文件表
CREATE TABLE upload_file (
uuid varchar(32) NOT NULL COMMENT '编号' primary key,
rel_obj_type varchar(50) DEFAULT NULL COMMENT '关联对象类',
rel_obj_id bigint DEFAULT NULL COMMENT '关联对象ID',
file_name varchar(100) NOT NULL COMMENT '文件名',
storage_path varchar(200) NOT NULL COMMENT '存储路径',
file_type varchar(20) DEFAULT NULL COMMENT '文件类型',
data_count int DEFAULT 0 COMMENT '数据量',
description varchar(100) DEFAULT NULL COMMENT '备注',
is_deleted tinyint(1) default 0 not null comment '是否删除',
create_time timestamp default CURRENT_TIMESTAMP not null comment '创建时间'
) DEFAULT CHARSET=utf8 COMMENT='上传文件';
-- 索引
create index idx_upload_file on upload_file (rel_obj_type, rel_obj_id);

View File

@ -0,0 +1,15 @@
-- 上传文件表
CREATE TABLE upload_file (
uuid varchar(32) NOT NULL COMMENT '编号' primary key,
rel_obj_type varchar(50) DEFAULT NULL COMMENT '关联对象类',
rel_obj_id bigint DEFAULT NULL COMMENT '关联对象ID',
file_name varchar(100) NOT NULL COMMENT '文件名',
storage_path varchar(200) NOT NULL COMMENT '存储路径',
file_type varchar(20) DEFAULT NULL COMMENT '文件类型',
data_count int DEFAULT 0 COMMENT '数据量',
description varchar(100) DEFAULT NULL COMMENT '备注',
is_deleted tinyint(1) default 0 not null comment '是否删除',
create_time timestamp default CURRENT_TIMESTAMP not null comment '创建时间'
) DEFAULT CHARSET=utf8 COMMENT='上传文件';
-- 索引
create index idx_upload_file on upload_file (rel_obj_type, rel_obj_id);

View File

@ -0,0 +1,28 @@
-- 上传文件表
CREATE TABLE ${SCHEMA}.upload_file (
uuid VARCHAR2(32) NOT NULL,
rel_obj_type VARCHAR2(50),
rel_obj_id NUMBER(20),
file_name VARCHAR2(100) NOT NULL,
storage_path VARCHAR2(200) NOT NULL,
file_type VARCHAR2(20),
data_count NUMBER(9) DEFAULT 0 not null,
description VARCHAR2(100),
is_deleted NUMBER(1) DEFAULT 0 not null,
create_time timestamp default CURRENT_TIMESTAMP not null,
constraint PK_upload_file primary key (uuid)
);
-- 添加备注,
comment on column ${SCHEMA}.upload_file.uuid is 'UUID';
comment on column ${SCHEMA}.upload_file.rel_obj_type is '关联对象类';
comment on column ${SCHEMA}.upload_file.rel_obj_id is '关联对象ID';
comment on column ${SCHEMA}.upload_file.file_name is '文件名';
comment on column ${SCHEMA}.upload_file.storage_path is '存储路径';
comment on column ${SCHEMA}.upload_file.file_type is '文件类型';
comment on column ${SCHEMA}.upload_file.data_count is '数据量';
comment on column ${SCHEMA}.upload_file.description is '备注';
comment on column ${SCHEMA}.upload_file.is_deleted is '删除标记';
comment on column ${SCHEMA}.upload_file.create_time is '创建时间';
comment on table ${SCHEMA}.upload_file is '上传文件';
-- 索引
create index idx_upload_file on ${SCHEMA}.upload_file (rel_obj_type, rel_obj_id);

View File

@ -0,0 +1,28 @@
-- 上传文件表
CREATE TABLE upload_file (
uuid varchar(32) NOT NULL,
rel_obj_type varchar(50),
rel_obj_id bigint,
file_name varchar(100) NOT NULL,
storage_path varchar(200) NOT NULL,
file_type varchar(20),
data_count int not null DEFAULT 0,
description varchar(100),
is_deleted BOOLEAN not null DEFAULT FALSE,
create_time timestamp not null default CURRENT_TIMESTAMP,
constraint PK_upload_file primary key (uuid)
);
-- 添加备注,
comment on column upload_file.uuid is 'UUID';
comment on column upload_file.rel_obj_type is '关联对象类';
comment on column upload_file.rel_obj_id is '关联对象ID';
comment on column upload_file.file_name is '文件名';
comment on column upload_file.storage_path is '存储路径';
comment on column upload_file.file_type is '文件类型';
comment on column upload_file.data_count is '数据量';
comment on column upload_file.description is '备注';
comment on column upload_file.is_deleted is '删除标记';
comment on column upload_file.create_time is '创建时间';
comment on table upload_file is '上传文件';
-- 索引
create index idx_upload_file on upload_file (rel_obj_type, rel_obj_id);

View File

@ -0,0 +1,28 @@
-- 上传文件表
CREATE TABLE ${SCHEMA}.upload_file (
uuid varchar(32) NOT NULL,
rel_obj_type varchar(50),
rel_obj_id bigint,
file_name varchar(100) NOT NULL,
storage_path varchar(200) NOT NULL,
file_type varchar(20),
data_count int not null DEFAULT 0,
description varchar(100),
is_deleted BOOLEAN not null DEFAULT FALSE,
create_time timestamp not null default CURRENT_TIMESTAMP,
constraint PK_upload_file primary key (uuid)
);
-- 添加备注,
execute sp_addextendedproperty 'MS_Description', N'UUID', 'SCHEMA', '${SCHEMA}', 'table', upload_file, 'column', 'uuid';
execute sp_addextendedproperty 'MS_Description', N'关联对象类', 'SCHEMA', '${SCHEMA}', 'table', upload_file, 'column', 'rel_obj_type';
execute sp_addextendedproperty 'MS_Description', N'关联对象ID', 'SCHEMA', '${SCHEMA}', 'table', upload_file, 'column', 'rel_obj_id';
execute sp_addextendedproperty 'MS_Description', N'文件名', 'SCHEMA', '${SCHEMA}', 'table', upload_file, 'column', 'file_name';
execute sp_addextendedproperty 'MS_Description', N'存储路径', 'SCHEMA', '${SCHEMA}', 'table', upload_file, 'column', 'storage_path';
execute sp_addextendedproperty 'MS_Description', N'文件类型', 'SCHEMA', '${SCHEMA}', 'table', upload_file, 'column', 'file_type';
execute sp_addextendedproperty 'MS_Description', N'数据量', 'SCHEMA', '${SCHEMA}', 'table', upload_file, 'column', 'data_count';
execute sp_addextendedproperty 'MS_Description', N'备注', 'SCHEMA', '${SCHEMA}', 'table', upload_file, 'column', 'description';
execute sp_addextendedproperty 'MS_Description', N'删除标记', 'SCHEMA', '${SCHEMA}', 'table', upload_file, 'column', 'is_deleted';
execute sp_addextendedproperty 'MS_Description', N'创建时间', 'SCHEMA', '${SCHEMA}', 'table', upload_file, 'column', 'create_time';
execute sp_addextendedproperty 'MS_Description', N'上传文件', 'SCHEMA', '${SCHEMA}', 'table', upload_file, null, null;
-- 索引
create nonclustered index idx_upload_file on upload_file(rel_obj_type, rel_obj_id);

View File

@ -30,7 +30,7 @@ import java.util.Map;
* @date 2019/08/01
*/
public class SqlHandler {
private static final Logger logger = LoggerFactory.getLogger(SqlHandler.class);
private static final Logger log = LoggerFactory.getLogger(SqlHandler.class);
// 数据字典SQL
private static final String DICTIONARY_SQL = "SELECT id FROM ${SCHEMA}.dictionary WHERE id=0";
@ -78,7 +78,7 @@ public class SqlHandler {
// 获取SqlSessionFactory实例
SqlSessionFactory sqlSessionFactory = (SqlSessionFactory) ContextHelper.getBean(SqlSessionFactory.class);
if(sqlSessionFactory == null){
logger.warn("无法获取SqlSessionFactory实例安装SQL将无法执行请手动安装");
log.warn("无法获取SqlSessionFactory实例安装SQL将无法执行请手动安装");
return false;
}
sqlStatement = buildPureSqlStatement(sqlStatement);
@ -174,11 +174,11 @@ public class SqlHandler {
try{
boolean success = SqlExecutor.executeUpdate(sqlStatement, null);
if(success){
logger.info("初始化SQL执行完成: "+ S.substring(sqlStatement, 0, 60) + "...");
log.info("初始化SQL执行完成: "+ S.substring(sqlStatement, 0, 60) + "...");
}
}
catch (Exception e){
logger.error("初始化SQL执行异常请检查或手动执行。SQL => "+sqlStatement, e);
log.error("初始化SQL执行异常请检查或手动执行。SQL => "+sqlStatement, e);
}
}
return true;
@ -196,10 +196,10 @@ public class SqlHandler {
lines = IOUtils.readLines(is, "UTF-8");
}
catch (FileNotFoundException fe){
logger.warn("暂未发现数据库SQL: "+sqlPath + " 请参考其他数据库定义DDL手动初始化。");
log.warn("暂未发现数据库SQL: "+sqlPath + " 请参考其他数据库定义DDL手动初始化。");
}
catch (Exception e){
logger.warn("读取SQL文件异常: "+sqlPath, e);
log.warn("读取SQL文件异常: "+sqlPath, e);
}
return lines;
}
@ -326,7 +326,7 @@ public class SqlHandler {
}
}
catch(Exception e){
logger.error("获取SqlServer默认Schema异常: {}", e.getMessage());
log.error("获取SqlServer默认Schema异常: {}", e.getMessage());
}
return null;
}

View File

@ -1,10 +1,14 @@
package com.diboot.core.util;
import org.hibernate.validator.HibernateValidator;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.validation.BindingResult;
import org.springframework.validation.ObjectError;
import javax.validation.ConstraintViolation;
import javax.validation.Validation;
import javax.validation.Validator;
import java.sql.Timestamp;
import java.util.*;
@ -17,6 +21,10 @@ import java.util.*;
*/
public class V {
private static final Logger log = LoggerFactory.getLogger(V.class);
/**
* hibernate注解验证
*/
private static Validator VALIDATOR = Validation.byProvider(HibernateValidator.class).configure().failFast(false).buildValidatorFactory().getValidator();
/***
* 对象是否为空
@ -234,6 +242,7 @@ public class V {
* @param validation
* @return
*/
@Deprecated
public static String validate(String value, String validation){
if(isEmpty(validation)){
return null;
@ -404,4 +413,23 @@ public class V {
return S.join(allErrors);
}
/**
* 功能描述: <br>
* 注解验证参数
*
* @param obj
*/
public static <T> String validateBean(T obj) {
// 校验
Set<ConstraintViolation<T>> errors = VALIDATOR.validate(obj);
if(errors == null || errors.size() == 0){
return null;
}
List<String> allErrors = new ArrayList<>(errors.size());
for(ConstraintViolation<T> err : errors){
allErrors.add(err.getMessage());
}
return S.join(allErrors);
}
}

View File

@ -7,7 +7,6 @@ import diboot.core.test.binder.entity.Department;
* 部门Mapper
* @author mazc@dibo.ltd
* @version 2018/12/22
* Copyright © www.dibo.ltd
*/
public interface DepartmentMapper extends BaseCrudMapper<Department> {

View File

@ -7,7 +7,6 @@ import diboot.core.test.binder.entity.Organization;
* 单位Mapper
* @author mazc@dibo.ltd
* @version 2018/12/22
* Copyright © www.dibo.ltd
*/
public interface OrganizationMapper extends BaseCrudMapper<Organization> {

View File

@ -7,7 +7,6 @@ import diboot.core.test.binder.entity.Role;
* 员工Mapper
* @author mazc@dibo.ltd
* @version 2018/12/22
* Copyright © www.dibo.ltd
*/
public interface RoleMapper extends BaseCrudMapper<Role> {

View File

@ -7,7 +7,6 @@ import diboot.core.test.binder.entity.User;
* 员工Mapper
* @author mazc@dibo.ltd
* @version 2018/12/22
* Copyright © www.dibo.ltd
*/
public interface UserMapper extends BaseCrudMapper<User> {

View File

@ -10,7 +10,6 @@ import org.springframework.stereotype.Service;
* 单位相关Service实现
* @author mazc@dibo.ltd
* @version 2018/12/23
* Copyright © www.dibo.ltd
*/
@Service
public class OrganizationServiceImpl extends BaseServiceImpl<OrganizationMapper, Organization> implements OrganizationService {

View File

@ -10,7 +10,6 @@ import org.springframework.stereotype.Service;
* 员工相关Service
* @author mazc@dibo.ltd
* @version 2018/12/23
* Copyright © www.dibo.ltd
*/
@Service
public class RoleServiceImpl extends ServiceImpl<RoleMapper, Role> implements RoleService {

View File

@ -10,7 +10,6 @@ import org.springframework.stereotype.Service;
* 员工相关Service
* @author mazc@dibo.ltd
* @version 2018/12/23
* Copyright © www.dibo.ltd
*/
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {

View File

@ -20,6 +20,7 @@
<module>diboot-core</module>
<module>diboot-core-starter</module>
<module>iam-base-starter</module>
<module>component-file-starter</module>
</modules>
<properties>