+ 文件组件代码重构
This commit is contained in:
parent
c9e2c934dc
commit
6ffae82640
|
@ -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>
|
|
@ -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
|
||||
}
|
||||
|
||||
}
|
|
@ -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格式文件!");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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()));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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));
|
||||
}
|
||||
}
|
|
@ -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());
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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> {
|
||||
|
||||
}
|
|
@ -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
|
||||
>
|
||||
|
||||
|
|
@ -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);
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
package com.diboot.component.file.starter;
|
||||
|
||||
import com.diboot.core.plugin.PluginManager;
|
||||
|
||||
public class FilePluginManager implements PluginManager {
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.diboot.component.file.starter.FileAutoConfiguration
|
|
@ -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);
|
|
@ -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);
|
|
@ -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);
|
|
@ -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);
|
|
@ -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);
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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> {
|
||||
|
||||
|
|
|
@ -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> {
|
||||
|
||||
|
|
|
@ -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> {
|
||||
|
||||
|
|
|
@ -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> {
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 {
|
||||
|
|
Loading…
Reference in New Issue