add:新增文件上传策略

This commit is contained in:
wuy 2020-05-20 12:53:16 +08:00
parent e3f3a0c674
commit e50d1a7c6f
11 changed files with 304 additions and 27 deletions

View File

@ -21,6 +21,7 @@ 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 com.diboot.file.dto.UploadFileFormDTO;
import com.diboot.file.entity.UploadFile;
import com.diboot.file.excel.listener.FixedHeadExcelListener;
import com.diboot.file.util.ExcelHelper;
@ -28,13 +29,13 @@ import com.diboot.file.util.FileHelper;
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
@ -58,11 +59,23 @@ public abstract class BaseExcelFileController extends BaseFileController {
* @throws Exception
*/
public JsonResult excelPreview(MultipartFile file) throws Exception {
Map<String, Object> dataMap = new HashMap();
Map<String, Object> dataMap = new HashMap(16);
savePreviewExcelFile(file, dataMap);
return JsonResult.OK(dataMap);
}
/***
* excel数据预览
* @param uploadFileFormDTO 带有其他配置的上传
* @return
* @throws Exception
*/
public JsonResult excelPreview(UploadFileFormDTO uploadFileFormDTO) throws Exception {
Map<String, Object> dataMap = new HashMap(16);
savePreviewExcelFile(uploadFileFormDTO, dataMap);
return JsonResult.OK(dataMap);
}
/***
* 预览后提交保存
* @param
@ -75,17 +88,46 @@ public abstract class BaseExcelFileController extends BaseFileController {
}
String fileUid = S.substringBefore(previewFileName, ".");
String fullPath = FileHelper.getFullPath(previewFileName);
String accessUrl = FileHelper.getRelativePath(previewFileName);
String ext = FileHelper.getFileExtByName(originFileName);
// 描述
String description = getString("description");
// 保存文件上传记录
UploadFile uploadFile = new UploadFile().setUuid(fileUid)
.setFileName(originFileName).setStoragePath(fullPath).setFileType(ext)
.setAccessUrl(accessUrl)
.setRelObjType(entityClass.getSimpleName()).setDescription(description);
super.createUploadFile(uploadFile);
return JsonResult.OK();
}
/***
* 预览后提交保存
* @param uploadFileFormDTO
* @param previewFileName
* @param originFileName
* @return
* @throws Exception
*/
public <T> JsonResult excelPreviewSave(UploadFileFormDTO uploadFileFormDTO, String previewFileName, String originFileName) 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 accessUrl = FileHelper.getRelativePath(previewFileName);
String ext = FileHelper.getFileExtByName(originFileName);
// 保存文件上传记录
UploadFile uploadFile = new UploadFile().setUuid(fileUid)
.setFileName(originFileName).setStoragePath(fullPath).setFileType(ext)
.setAccessUrl(accessUrl)
.setRelObjField(uploadFileFormDTO.getRelObjField())
.setRelObjType(uploadFileFormDTO.getRelObjType())
.setDescription(uploadFileFormDTO.getDescription());
super.createUploadFile(uploadFile);
return JsonResult.OK();
}
/***
* 直接上传excel
* @param
@ -97,8 +139,20 @@ public abstract class BaseExcelFileController extends BaseFileController {
return super.uploadFile(file, entityClass);
}
/***
* 直接上传excel
* @param
* @return
* @throws Exception
*/
public <T> JsonResult uploadExcelFile(UploadFileFormDTO uploadFileFormDTO) throws Exception {
checkIsExcel(uploadFileFormDTO.getFile());
return super.uploadFile(uploadFileFormDTO);
}
/**
* 保存上传文件
*
* @return
* @throws Exception
*/
@ -111,8 +165,7 @@ public abstract class BaseExcelFileController extends BaseFileController {
listener.setRequestParams(super.getParamsMap());
try {
ExcelHelper.previewReadExcel(uploadFile.getStoragePath(), listener);
}
catch (Exception e) {
} catch (Exception e) {
log.warn("解析并校验excel文件失败", e);
if (V.notEmpty(e.getMessage())) {
throw new Exception(e.getMessage());
@ -131,6 +184,40 @@ public abstract class BaseExcelFileController extends BaseFileController {
dataMap.put("dataList", dataList);
}
/**
* 保存上传文件
*
* @return
* @throws Exception
*/
private void savePreviewExcelFile(UploadFileFormDTO uploadFileFormDTO, Map<String, Object> dataMap) throws Exception {
checkIsExcel(uploadFileFormDTO.getFile());
// 保存文件到本地
UploadFile uploadFile = super.saveFile(uploadFileFormDTO);
// 预览
FixedHeadExcelListener listener = getExcelDataListener();
listener.setRequestParams(super.getParamsMap());
try {
ExcelHelper.previewReadExcel(uploadFile.getStoragePath(), listener);
} catch (Exception e) {
log.warn("解析并校验excel文件失败", e);
if (V.notEmpty(e.getMessage())) {
throw new Exception(e.getMessage());
}
throw e;
}
// 绑定属性到model
dataMap.put("header", listener.getFieldHeaders());
dataMap.put(ORIGIN_FILE_NAME, uploadFileFormDTO.getFile().getOriginalFilename());
dataMap.put(PREVIEW_FILE_NAME, FileHelper.getFileName(uploadFile.getStoragePath()));
List dataList = listener.getDataList();
if (V.notEmpty(dataList) && dataList.size() > BaseConfig.getPageSize()) {
dataList = dataList.subList(0, BaseConfig.getPageSize());
}
//最多返回前端十条数据
dataMap.put("dataList", dataList);
}
/**
* 保存文件之后的处理逻辑如解析excel
*/
@ -142,8 +229,7 @@ public abstract class BaseExcelFileController extends BaseFileController {
listener.setRequestParams(super.getParamsMap());
try {
ExcelHelper.readAndSaveExcel(fullPath, listener);
}
catch(Exception e){
} catch (Exception e) {
log.warn("上传数据错误: " + e.getMessage(), e);
if (V.notEmpty(e.getMessage())) {
throw new Exception(e.getMessage());
@ -155,6 +241,7 @@ public abstract class BaseExcelFileController extends BaseFileController {
/**
* 检查是否为合法的excel文件
*
* @param file
* @throws Exception
*/

View File

@ -16,6 +16,8 @@
package com.diboot.file.controller;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.diboot.core.controller.BaseController;
import com.diboot.core.exception.BusinessException;
import com.diboot.core.util.S;
@ -23,14 +25,18 @@ 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 com.diboot.file.dto.UploadFileFormDTO;
import com.diboot.file.entity.UploadFile;
import com.diboot.file.service.UploadFileService;
import com.diboot.file.util.FileHelper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletRequest;
import javax.validation.constraints.NotNull;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@ -81,10 +87,12 @@ public abstract class BaseFileController extends BaseController {
// 保存上传记录
createUploadFile(uploadFile);
// 返回结果
Map<String, String> dataMap = new HashMap<>();
dataMap.put("uuid", uploadFile.getUuid());
dataMap.put("accessUrl", uploadFile.getAccessUrl());
return JsonResult.OK(dataMap);
// 返回结果
return JsonResult.OK(new HashMap(16) {{
put("uuid", uploadFile.getUuid());
put("accessUrl", uploadFile.getAccessUrl());
put("fileName", uploadFile.getFileName());
}});
}
/**
@ -114,6 +122,59 @@ public abstract class BaseFileController extends BaseController {
return uploadFile;
}
/***
* 直接上传文件
* @param uploadFileFormDTO
* @return
* @throws Exception
*/
public JsonResult uploadFile(UploadFileFormDTO uploadFileFormDTO) throws Exception {
if(uploadFileFormDTO == null || uploadFileFormDTO.getFile() == null) {
throw new BusinessException(Status.FAIL_INVALID_PARAM, "未获取待处理的文件!");
}
String originFileName = uploadFileFormDTO.getFile().getOriginalFilename();
if (V.isEmpty(originFileName) || !FileHelper.isValidFileExt(originFileName)) {
log.debug("非法的文件类型: " + originFileName);
throw new BusinessException(Status.FAIL_VALIDATION, "请上传合法的文件格式!");
}
// 保存文件
UploadFile uploadFile = saveFile(uploadFileFormDTO);
// 保存上传记录
createUploadFile(uploadFile);
// 返回结果
return JsonResult.OK(new HashMap(16) {{
put("uuid", uploadFile.getUuid());
put("accessUrl", uploadFile.getAccessUrl());
put("fileName", uploadFile.getFileName());
}});
}
/**
* 保存文件
* @param uploadFileFormDTO
* @param <T>
* @return
* @throws Exception
*/
protected <T> UploadFile saveFile(UploadFileFormDTO uploadFileFormDTO) throws Exception{
// 文件后缀
String originFileName = uploadFileFormDTO.getFile().getOriginalFilename();
String ext = FileHelper.getFileExtByName( uploadFileFormDTO.getFile().getOriginalFilename());
// 先保存文件
String fileUid = S.newUuid();
String newFileName = fileUid + "." + ext;
String storageFullPath = FileHelper.saveFile( uploadFileFormDTO.getFile(), newFileName);
String accessUrl = FileHelper.getRelativePath(newFileName);
UploadFile uploadFile = new UploadFile();
uploadFile.setUuid(fileUid).setFileName(originFileName).setFileType(ext);
uploadFile.setRelObjType(uploadFileFormDTO.getRelObjType())
.setRelObjField(uploadFileFormDTO.getRelObjField())
.setStoragePath(storageFullPath).setAccessUrl(accessUrl)
.setDescription(uploadFileFormDTO.getDescription());
// 返回uploadFile对象
return uploadFile;
}
/**
* 保存上传文件信息
* @param uploadFile
@ -127,6 +188,29 @@ public abstract class BaseFileController extends BaseController {
uploadFileService.createEntity(uploadFile);
}
/**
* <h3>获取文件通用接口</h3>
* <p>
* 其中当relObjField不传递的时候表示获取当前业务ID和业务类型下的所有文件<br/>
* 当传递relObjField的时候获取指定类型的文件
* </p>
*
* @param relObjId 业务ID <strong style="color:red;">必传字段</strong>
* @param relObjType 业务类型 <strong style="color:red;">必传字段</strong>
* @param relObjField 对应的具体类型 <strong style="color:blue;">非必传字段(同一种业务下可能有多种文件)</strong>
* @return {@link List <UploadFile>} 返回文件对象的集合
* @throws Exception
*/
public List<UploadFile> getUploadFileList(Long relObjId, String relObjType, String relObjField) throws Exception {
LambdaQueryWrapper<UploadFile> wrapper = Wrappers.<UploadFile>lambdaQuery()
.eq(UploadFile::getRelObjId, relObjId)
.eq(UploadFile::getRelObjType, relObjType);
if (V.notEmpty(relObjField)) {
wrapper.eq(UploadFile::getRelObjField, relObjField);
}
return uploadFileService.getEntityList(wrapper);
}
/**
* 保存文件之后的处理逻辑如解析excel
*/

View File

@ -0,0 +1,47 @@
package com.diboot.file.dto;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import org.springframework.web.multipart.MultipartFile;
import javax.validation.constraints.NotNull;
import java.io.Serializable;
/**
* 上传文件提交的form
*
* @author : uu
* @version : v1.0
* @Date 2020-05-18 17:32
*/
@Setter
@Getter
@ToString
public class UploadFileFormDTO implements Serializable {
private static final long serialVersionUID = -3467553770266812902L;
/**
* 上传的文件
*/
@NotNull(message = "上传文件不能为空")
private MultipartFile file;
/**
* 关联对象类
*/
@NotNull(message = "关联对象类不能为空!")
private String relObjType;
/**
* 关联对象属性
*/
@NotNull(message = "关联对象属性不能为空!")
private String relObjField;
/**
* 描述
*/
private String description;
}

View File

@ -51,6 +51,10 @@ public class UploadFile extends BaseEntity {
@NotNull(message = "关联对象ID不能为空")
private Long relObjId;
@TableField
@NotNull(message = "关联对象属性不能为空!")
private String relObjField;
@TableField
@NotNull(message = "文件名不能为空!")
@Length(max = 100, message = "文件名长度超出了最大限制!")

View File

@ -15,6 +15,7 @@
*/
package com.diboot.file.service;
import com.diboot.core.entity.BaseEntity;
import com.diboot.core.service.BaseService;
import com.diboot.file.entity.UploadFile;
@ -22,6 +23,7 @@ import java.util.List;
/**
* 基础文件Service
*
* @author Lishuaifei@dibo.ltd
* @date 2019-07-18
*/
@ -29,10 +31,21 @@ public interface UploadFileService extends BaseService<UploadFile> {
/**
* 获取指定对象记录关联的上传文件列表
*
* @param relObjClass
* @param relObjId
* @return
*/
List<UploadFile> getUploadedFiles(String relObjClass, Long relObjId);
/**
* 绑定业务id
*
* @param relObjId
* @param relObjTypeClass
* @param fileUuidList
* @throws Exception
*/
void bindRelObjId(Long relObjId, Class<?> relObjTypeClass, List<String> fileUuidList) throws Exception;
}

View File

@ -17,11 +17,15 @@ package com.diboot.file.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.diboot.core.entity.BaseEntity;
import com.diboot.core.service.impl.BaseServiceImpl;
import com.diboot.core.util.V;
import com.diboot.file.entity.UploadFile;
import com.diboot.file.mapper.UploadFileMapper;
import com.diboot.file.service.UploadFileService;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
@ -42,4 +46,34 @@ public class UploadFileServiceImpl extends BaseServiceImpl<UploadFileMapper, Upl
return getEntityList(queryWrapper);
}
@Override
@Transactional(rollbackFor = Exception.class)
public void bindRelObjId(Long relObjId, Class<?> relObjTypeClass, List<String> fileUuidList) throws Exception {
//如果不存在需要绑定的么清除所有当前关联的所有文件
if (V.isEmpty(fileUuidList)) {
this.update(
Wrappers.<UploadFile>lambdaUpdate()
.set(true, UploadFile::isDeleted, true)
.eq(UploadFile::getRelObjId, relObjId)
.eq(UploadFile::getRelObjType, relObjTypeClass.getSimpleName())
);
return;
}
// 删除 relObjId + relObjType下的 并且不包含fileIdList的file
this.update(
Wrappers.<UploadFile>lambdaUpdate()
.set(true, UploadFile::isDeleted, true)
.eq(UploadFile::getRelObjId, relObjId)
.eq(UploadFile::getRelObjType, relObjTypeClass.getSimpleName())
.notIn(UploadFile::getUuid, fileUuidList)
);
// 绑定绑定数据
this.update(
Wrappers.<UploadFile>lambdaUpdate()
.set(true, UploadFile::getRelObjId, relObjId)
.in(UploadFile::getUuid, fileUuidList)
);
}
}

View File

@ -3,6 +3,7 @@ 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',
rel_obj_field varchar(50) DEFAULT NULL COMMENT '关联对象属性名称',
file_name varchar(100) NOT NULL COMMENT '文件名',
storage_path varchar(200) NOT NULL COMMENT '存储路径',
file_type varchar(20) DEFAULT NULL COMMENT '文件类型',
@ -13,4 +14,4 @@ CREATE TABLE upload_file (
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);
create index idx_upload_file on upload_file (rel_obj_type, rel_obj_id, rel_obj_field);

View File

@ -3,6 +3,7 @@ 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',
rel_obj_field varchar(50) DEFAULT NULL COMMENT '关联对象属性名称',
file_name varchar(100) NOT NULL COMMENT '文件名',
storage_path varchar(200) NOT NULL COMMENT '存储路径',
access_url varchar(200) NULL COMMENT '访问地址',
@ -13,4 +14,4 @@ CREATE TABLE upload_file (
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);
create index idx_upload_file on upload_file (rel_obj_type, rel_obj_id, rel_obj_field);

View File

@ -3,6 +3,7 @@ CREATE TABLE ${SCHEMA}.upload_file (
uuid VARCHAR2(32) NOT NULL,
rel_obj_type VARCHAR2(50),
rel_obj_id NUMBER(20),
rel_obj_field VARCHAR2(50),
file_name VARCHAR2(100) NOT NULL,
storage_path VARCHAR2(200) NOT NULL,
access_url VARCHAR2(200),
@ -17,6 +18,7 @@ CREATE TABLE ${SCHEMA}.upload_file (
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.rel_obj_field is '关联对象属性名称';
comment on column ${SCHEMA}.upload_file.file_name is '文件名';
comment on column ${SCHEMA}.upload_file.storage_path is '存储路径';
comment on column ${SCHEMA}.upload_file.access_url is '访问地址';
@ -27,4 +29,4 @@ 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);
create index idx_upload_file on ${SCHEMA}.upload_file (rel_obj_type, rel_obj_id, rel_obj_field);

View File

@ -3,6 +3,7 @@ CREATE TABLE upload_file (
uuid varchar(32) NOT NULL,
rel_obj_type varchar(50),
rel_obj_id bigint,
rel_obj_field varchar(50),
file_name varchar(100) NOT NULL,
storage_path varchar(200) NOT NULL,
access_url varchar(200),
@ -17,6 +18,7 @@ CREATE TABLE upload_file (
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.rel_obj_field is '关联对象属性名称';
comment on column upload_file.file_name is '文件名';
comment on column upload_file.storage_path is '存储路径';
comment on column upload_file.access_url is '访问地址';
@ -27,4 +29,4 @@ 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);
create index idx_upload_file on upload_file (rel_obj_type, rel_obj_id, rel_obj_field);

View File

@ -3,6 +3,7 @@ CREATE TABLE ${SCHEMA}.upload_file (
uuid varchar(32) NOT NULL,
rel_obj_type varchar(50),
rel_obj_id bigint,
rel_obj_field varchar(50),
file_name varchar(100) NOT NULL,
storage_path varchar(200) NOT NULL,
access_url varchar(200) NULL,
@ -17,6 +18,7 @@ CREATE TABLE ${SCHEMA}.upload_file (
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', 'rel_obj_field';
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', 'access_url';
@ -27,4 +29,4 @@ execute sp_addextendedproperty 'MS_Description', N'删除标记', 'SCHEMA', '${S
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);
create nonclustered index idx_upload_file on upload_file(rel_obj_type, rel_obj_id, rel_obj_field);