实现了部分接口,提交备份。

This commit is contained in:
chenxudong 2024-12-20 14:15:07 +08:00
parent 926f0ae96e
commit 12a4d198a6
8 changed files with 396 additions and 37 deletions

View File

@ -1,14 +1,14 @@
package com.electromagnetic.industry.software.manage.controller;
import com.electromagnetic.industry.software.common.resp.ElectromagneticResult;
import com.electromagnetic.industry.software.manage.pojo.req.FileChunkDTO;
import com.electromagnetic.industry.software.manage.pojo.req.FileInfoQueryDTO;
import com.electromagnetic.industry.software.manage.pojo.req.UpdateFileInfoDTO;
import com.electromagnetic.industry.software.manage.service.EdFileInfoService;
import io.swagger.annotations.ApiOperation;
import org.springframework.core.io.InputStreamResource;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
@ -52,13 +52,8 @@ public class EdFileInfoController {
}
@RequestMapping("updateFileInfo")
public ElectromagneticResult<?> updateFileInfo() {
return null;
}
@RequestMapping("deleteFile")
public ElectromagneticResult<?> deleteFile() {
return null;
public ElectromagneticResult<?> updateFileInfo(@RequestBody UpdateFileInfoDTO updateFileInfoDTO) {
return edFileInfoService.updateFileInfo(updateFileInfoDTO);
}
@RequestMapping("moveFile")
@ -77,12 +72,29 @@ public class EdFileInfoController {
}
@RequestMapping("versionBack")
public ElectromagneticResult<?> versionBack() {
return null;
public ElectromagneticResult<?> versionBack(@RequestParam String fileId, @RequestParam int targetVersion) {
return edFileInfoService.versionBack(fileId, targetVersion);
}
@RequestMapping("batchExport")
public ElectromagneticResult<?> batchExport() {
return null;
public ResponseEntity<InputStreamResource> batchExport(@RequestParam String dataIdArr, HttpServletResponse response) throws IOException {
return edFileInfoService.batchExport(dataIdArr, response);
}
@RequestMapping(value = "/mergeChunks",method = RequestMethod.GET)
public ElectromagneticResult<?> mergeChunks(@RequestParam String identifier,
@RequestParam String fileName,
@RequestParam Integer totalChunks) {
return edFileInfoService.mergeChunks(identifier, fileName, totalChunks);
}
@RequestMapping(value = "/batchImport",method = RequestMethod.POST)
public ElectromagneticResult<?> batchImport(FileChunkDTO fileChunkDTO) {
return edFileInfoService.batchImport(fileChunkDTO);
}
@RequestMapping(value = "/batchImport",method = RequestMethod.GET)
public ElectromagneticResult<?> checkChunkExist(FileChunkDTO fileChunkDTO) {
return edFileInfoService.checkChunkExist(fileChunkDTO);
}
}

View File

@ -0,0 +1,12 @@
package com.electromagnetic.industry.software.manage.pojo.req;
import lombok.Data;
@Data
public class UpdateFileInfoDTO {
private String id;
private String fileName;
private String fileNote;
}

View File

@ -1,7 +1,9 @@
package com.electromagnetic.industry.software.manage.service;
import com.electromagnetic.industry.software.common.resp.ElectromagneticResult;
import com.electromagnetic.industry.software.manage.pojo.req.FileChunkDTO;
import com.electromagnetic.industry.software.manage.pojo.req.FileInfoQueryDTO;
import com.electromagnetic.industry.software.manage.pojo.req.UpdateFileInfoDTO;
import org.springframework.core.io.InputStreamResource;
import org.springframework.http.ResponseEntity;
@ -46,4 +48,48 @@ public interface EdFileInfoService {
* @return
*/
ResponseEntity<InputStreamResource> download(String id, HttpServletResponse response);
/**
* 更新文件信息
* @param updateFileInfoDTO
*/
ElectromagneticResult<?> updateFileInfo(UpdateFileInfoDTO updateFileInfoDTO);
/**
* 版本回退
* @param fileId
* @param targetVersion
* @return
*/
ElectromagneticResult<?> versionBack(String fileId, int targetVersion);
/**
* 查看分片是否存在
* @param fileChunkDTO
* @return
*/
ElectromagneticResult<?> checkChunkExist(FileChunkDTO fileChunkDTO);
/**
* 批量导入
* @param fileChunkDTO
* @return
*/
ElectromagneticResult<?> batchImport(FileChunkDTO fileChunkDTO);
/**
* 合并分片
* @param identifier
* @param fileName
* @param totalChunks
* @return
*/
ElectromagneticResult<?> mergeChunks(String identifier, String fileName, Integer totalChunks);
/**
* 导出
* @param dataIdArr
* @return
*/
ResponseEntity<InputStreamResource> batchExport(String dataIdArr, HttpServletResponse response) throws IOException;
}

View File

@ -33,6 +33,7 @@ import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.io.File;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;
import static com.electromagnetic.industry.software.common.cons.ElectromagneticConstants.*;
@ -42,6 +43,9 @@ public class CommonService {
private final EleLog log = new EleLog(CommonService.class);
private static final Map<String, String> FILE_TYPE_ENUM = new HashMap<>();
private static final Map<String, String> ID_NAME = new ConcurrentHashMap<>();
@Value("${prj.folder.max.length}")
private int prjFolderMaxLength;
@ -98,11 +102,19 @@ public class CommonService {
public String getFileSysPath(String dbPath) {
ArrayList<String> paths = CollUtil.newArrayList(dbPath.split(MYSQL_FILE_PATH_SPLIT));
String path = getPath(paths);
String path = getDbPath(paths);
return eleDataPath + File.separator + path;
}
public String getPath(List<String> ids) {
public String getDbPath(String dbPath) {
List<String> paths = CollUtil.newArrayList(dbPath.split(MYSQL_FILE_PATH_SPLIT));
return getDbPath(paths);
}
public String getDbPath(List<String> ids) {
// TODO cache
LambdaQueryWrapper<EdFileInfo> queryWrapper = Wrappers.lambdaQuery(EdFileInfo.class)
.select(EdFileInfo::getId, EdFileInfo::getFileName)
.in(EdFileInfo::getId, ids);
@ -128,7 +140,7 @@ public class CommonService {
.likeRight(EdFileInfo::getFilePath, id));
}
public ElectromagneticResult<?> addFolder(String parentId, String folderName, boolean maxLengthCheck, boolean isPrjDir) {
public ElectromagneticResult<?> addFolder(String parentId, String folderName, boolean maxLengthCheck, boolean isPrjDir, String folderId) {
// 验证名称是否合法
Assert.isTrue(EleCommonUtil.isFileNameValid(folderName), "文件名不符合规范只能包含中文字符、下划线、连字符、加号、数字和英文字符且长度小于32。");
@ -165,15 +177,13 @@ public class CommonService {
}
try {
int id = Integer.parseInt(edFileInfoMapper.maxPrjId());
Date now = new Date();
String currentUserId = UserThreadLocal.getUserId();
String newFolderId = String.valueOf(id + 1);
String path = currentPath + MYSQL_FILE_PATH_SPLIT + newFolderId;
String path = currentPath + MYSQL_FILE_PATH_SPLIT + folderId;
EdFileInfo fileInfo = new EdFileInfo();
String nowTimeStr = EleCommonUtil.getNowTimeStr();
fileInfo.setId(newFolderId)
.setFileId(newFolderId)
fileInfo.setId(folderId)
.setFileId(folderId)
.setFileName(folderName)
.setFileVersion(FILE_START_VERSION)
.setParentId(parentId)
@ -192,7 +202,7 @@ public class CommonService {
.setUpdatedBy(currentUserId);
edFileInfoMapper.insert(fileInfo);
// 保存到文件系统
String targetFilePath = getEleDataPath() + File.separator + getPath(paths) + File.separator + folderName;
String targetFilePath = getEleDataPath() + File.separator + getDbPath(paths) + File.separator + folderName;
fileSystemService.createDirectory(targetFilePath);
return ElectromagneticResultUtil.success(true);
} catch (Exception e) {

View File

@ -2,44 +2,84 @@ package com.electromagnetic.industry.software.manage.service.serviceimpl;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.codec.Base64;
import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.text.StrFormatter;
import cn.hutool.core.util.StrUtil;
import cn.hutool.core.util.ZipUtil;
import cn.hutool.crypto.SecureUtil;
import cn.hutool.crypto.symmetric.AES;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.electromagnetic.industry.software.common.enums.EleDataTypeEnum;
import com.electromagnetic.industry.software.common.exception.BizException;
import com.electromagnetic.industry.software.common.resp.ElectromagneticResult;
import com.electromagnetic.industry.software.common.util.EleLog;
import com.electromagnetic.industry.software.common.util.ElectromagneticResultUtil;
import com.electromagnetic.industry.software.common.util.IdWorker;
import com.electromagnetic.industry.software.common.util.UserThreadLocal;
import com.electromagnetic.industry.software.manage.mapper.EdFileInfoMapper;
import com.electromagnetic.industry.software.manage.pojo.models.EdFileInfo;
import com.electromagnetic.industry.software.manage.pojo.other.FileInfoDTO;
import com.electromagnetic.industry.software.manage.pojo.req.FileChunkDTO;
import com.electromagnetic.industry.software.manage.pojo.req.FileChunkResultDTO;
import com.electromagnetic.industry.software.manage.pojo.req.FileInfoQueryDTO;
import com.electromagnetic.industry.software.manage.pojo.req.UpdateFileInfoDTO;
import com.electromagnetic.industry.software.manage.pojo.resp.FileInfoQueryPageVO;
import com.electromagnetic.industry.software.manage.service.EdFileInfoService;
import com.electromagnetic.industry.software.manage.service.FileSystemService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.env.Environment;
import org.springframework.core.io.FileSystemResource;
import org.springframework.core.io.InputStreamResource;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.net.BindException;
import java.util.List;
import java.util.Objects;
import java.io.*;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.*;
import java.util.stream.Collectors;
import static com.electromagnetic.industry.software.common.cons.ElectromagneticConstants.*;
@Service
public class EdFileInfoServiceImpl extends ServiceImpl<EdFileInfoMapper, EdFileInfo> implements EdFileInfoService {
@Resource
private CommonService commonService;
private EleLog log = new EleLog(EdFileInfoServiceImpl.class);
@Autowired
private FileSystemService fileSystemService;
@Resource
private Environment environment;
private String downloadDataDir = "";
private String uploadDataDir = "";
@Value("${file.security.passwd}")
private String password;
@PostConstruct
public void init() {
String osName = System.getProperty("os.name").toLowerCase();
uploadDataDir = osName.startsWith("win") ? environment.getProperty("data.upload.windows.tmp.path") : environment.getProperty("data.upload.linux.tmp.path");
downloadDataDir = osName.startsWith("win") ? environment.getProperty("data.download.windows.tmp.path") : environment.getProperty("data.download.linux.tmp.path");
}
/**
* 查询文件列表
@ -86,12 +126,12 @@ public class EdFileInfoServiceImpl extends ServiceImpl<EdFileInfoMapper, EdFileI
*/
@Override
public ElectromagneticResult<?> createFolder(String parentId, String newFolderName) {
return commonService.addFolder(parentId, newFolderName, false, false);
String folderId = IdWorker.getSnowFlakeIdString();
return commonService.addFolder(parentId, newFolderName, false, false, folderId);
}
/**
* 项目层级结构查询
*
* @return
*/
@Override
@ -121,7 +161,6 @@ public class EdFileInfoServiceImpl extends ServiceImpl<EdFileInfoMapper, EdFileI
/**
* 下载文件
*
* @param id
* @return
*/
@ -152,4 +191,237 @@ public class EdFileInfoServiceImpl extends ServiceImpl<EdFileInfoMapper, EdFileI
throw new BizException(-1, info);
}
}
/**
* 更新文件信息
* @param updateFileInfoDTO
*/
@Override
@Transactional(rollbackFor = Exception.class)
public ElectromagneticResult<?> updateFileInfo(UpdateFileInfoDTO updateFileInfoDTO) {
try {
EdFileInfo fileInfo = this.baseMapper.selectById(updateFileInfoDTO.getId());
this.baseMapper.update(null, Wrappers.lambdaUpdate(EdFileInfo.class)
.eq(EdFileInfo::getId, updateFileInfoDTO.getId())
.set(EdFileInfo::getFileName, updateFileInfoDTO.getFileName())
.set(EdFileInfo::getFileNote, updateFileInfoDTO.getFileNote()));
String sysFilePath = commonService.getFileSysPath(fileInfo.getFilePath());
fileSystemService.renameFile(sysFilePath, updateFileInfoDTO.getFileName());
return ElectromagneticResultUtil.success(true);
} catch (Exception e) {
String info = "更新文件信息失败。";
log.error(info, e);
throw new BizException(-1, e.getMessage());
}
}
/**
* 版本回退
*
* @param fileId
* @param targetVersion
* @return
*/
@Override
@Transactional(rollbackFor = Exception.class)
public ElectromagneticResult<?> versionBack(String fileId, int targetVersion) {
try {
this.baseMapper.update(null, Wrappers.lambdaUpdate(EdFileInfo.class)
.set(EdFileInfo::getFileId, fileId)
.set(EdFileInfo::getEffectFlag, false));
this.baseMapper.update(null, Wrappers.lambdaUpdate(EdFileInfo.class)
.set(EdFileInfo::getEffectFlag, true)
.eq(EdFileInfo::getFileId, fileId)
.eq(EdFileInfo::getFileVersion, targetVersion));
} catch (Exception e) {
String info = "版本回退失败。";
log.error(info, e);
throw new BizException(-1, info);
}
return ElectromagneticResultUtil.success(true);
}
/**
* 查看分片是否存在
* @param fileChunkDTO
* @return
*/
@Override
public ElectromagneticResult<?> checkChunkExist(FileChunkDTO fileChunkDTO) {
String currentUserId = UserThreadLocal.getUserId();
String userUploadFolder = uploadDataDir + File.separator + currentUserId;
String identifier = fileChunkDTO.getIdentifier();
List<Integer> uploadedChunks = getUploadedChunks(identifier, userUploadFolder);
return ElectromagneticResultUtil.success(new FileChunkResultDTO(false, new HashSet<>(uploadedChunks)));
}
private List<Integer> getUploadedChunks(String identifier, String destPath) {
String filePath = destPath + File.separator + identifier;
if (!FileUtil.exist(new File(filePath))) {
return new ArrayList<>();
}
return FileUtil.listFileNames(destPath)
.stream()
.map(e -> e.replace(UPLOAD_FILE_CHUNK_SUFFIX, ""))
.map(Integer::parseInt)
.collect(Collectors.toList());
}
/**
* 批量导入
* @param fileChunkDTO
* @return
*/
@Override
public ElectromagneticResult<?> batchImport(FileChunkDTO fileChunkDTO) {
String currentUserId = UserThreadLocal.getUserId();
String identifier = fileChunkDTO.getIdentifier();
// 首先检查该分片有没被上传如果有则禁止上传
String destPath = uploadDataDir + File.separator + currentUserId + File.separator + identifier + fileChunkDTO.getChunkNumber() + UPLOAD_FILE_CHUNK_SUFFIX;
boolean exist = FileUtil.exist(destPath);
if (exist) {
return ElectromagneticResultUtil.fail("-1", "文件已经存在,请勿重复上传");
}
try (
InputStream inputStream = fileChunkDTO.getFile().getInputStream();
FileOutputStream fileOutputStream = new FileOutputStream(destPath);
) {
IoUtil.copy(inputStream, fileOutputStream);
} catch (IOException ioException) {
log.error("上传文件失败...", ioException);
throw new BizException(-1, "上传文件失败");
}
return ElectromagneticResultUtil.success(fileChunkDTO.getIdentifier());
}
/**
* 合并分片
* @param identifier
* @param fileName
* @param totalChunks
* @return
*/
@Override
public ElectromagneticResult<?> mergeChunks(String identifier, String fileName, Integer totalChunks) {
String destZipPath = doSysFileMerge(identifier, fileName, totalChunks);
int index = destZipPath.lastIndexOf(".zip");
String zipDirPath = destZipPath.substring(0, index);
AES aes = SecureUtil.aes(password.getBytes());
try(
InputStream inputStream = Files.newInputStream(Paths.get(destZipPath));
OutputStream outputStream = Files.newOutputStream(Paths.get(zipDirPath));
) {
aes.decrypt(inputStream, outputStream, true);
} catch (Exception e) {
String info = "文件上传错误";
log.error(info, e);
throw new BizException(-1, e.getMessage());
}
update2Database(zipDirPath);
return null;
}
/**
* 导出
*
* @param dataIdArr
* @return
*/
@Override
public ResponseEntity<InputStreamResource> batchExport(String dataIdArr, HttpServletResponse response) throws IOException {
String[] ids = dataIdArr.split(",");
List<EdFileInfo> resFiles = new ArrayList<>();
for (String id : ids) {
List<EdFileInfo> edFileInfos = this.baseMapper.selectList(Wrappers.lambdaQuery(EdFileInfo.class)
.like(EdFileInfo::getFilePath, MYSQL_FILE_PATH_SPLIT + id + MYSQL_FILE_PATH_SPLIT)
.eq(EdFileInfo::getEffectFlag, true));
resFiles.addAll(edFileInfos);
}
String prjName = resFiles.stream().filter(e -> e.getParentId().equals(PRJ_PARENT_ID)).findFirst().get().getFileName();
List<EdFileInfo> folders = resFiles.stream().filter(e -> e.getDataType().equals(EleDataTypeEnum.FOLDER.code)).collect(Collectors.toList());
List<EdFileInfo> files = resFiles.stream().filter(e -> e.getDataType().equals(EleDataTypeEnum.FILE.code)).collect(Collectors.toList());
for (EdFileInfo edFileInfo : folders) {
String destFolderPath = downloadDataDir + File.separator + commonService.getFileSysPath(edFileInfo.getFileId());
FileUtil.mkdir(destFolderPath);
}
for (EdFileInfo edFileInfo : files) {
String filePath = commonService.getFileSysPath(edFileInfo.getFileId()) + edFileInfo.getFileCode() + "." + edFileInfo.getFileCode();
String destPath = downloadDataDir + File.separator + commonService.getDbPath(edFileInfo.getFileId());
FileUtil.copy(filePath, destPath, false);
}
String mysqlInfo = getMysqlInfo();
FileUtil.writeString(mysqlInfo, downloadDataDir + File.separator + "mysql.info", Charset.defaultCharset());
String exportZipFile = downloadDataDir + File.separator + prjName + ".zip";
String exportColibFile = downloadDataDir + File.separator + prjName + EXPORT_FILE_SUFFIX;
ZipUtil.zip(downloadDataDir + File.separator + prjName, exportZipFile);
AES aes = SecureUtil.aes(password.getBytes());
try(
InputStream inputStream = Files.newInputStream(Paths.get(exportZipFile));
OutputStream outputStream = Files.newOutputStream(Paths.get(exportColibFile));
) {
aes.encrypt(inputStream, outputStream, true);
} catch (Exception e) {
String info = "导出失败。";
log.error(info, e);
throw new BizException(-1, info);
} finally {
FileUtil.del(exportZipFile);
}
File file = new File(exportColibFile);
FileSystemResource fileSystemResource = new FileSystemResource(file);
HttpHeaders headers = new HttpHeaders();
headers.add("Cache-Control", "no-cache, no-store, must-revalidate");
String fileName = Base64.encode(fileSystemResource.getFilename());
headers.add("Pragma", "no-cache");
headers.add("Expires", "0");
response.setHeader("content-disposition","attachment;filename=" + fileName);
// 构建响应实体(可以返回<byte[]或Resource返回类型取决body入参类型)
return ResponseEntity
.ok()
.headers(headers)
.contentLength(fileSystemResource.contentLength())
.contentType(MediaType.parseMediaType("application/octet-stream"))
.body(new InputStreamResource(fileSystemResource.getInputStream()));
}
private String getMysqlInfo() {
return "";
}
private void update2Database(String zipDirPath) {
// TODO 注意 需要reversion
}
private String doSysFileMerge(String identifier, String fileName, Integer totalChunks) {
String currentUserId = UserThreadLocal.getUserId();
for (int i = 1; i <= totalChunks; i++) {
String tmpPath = uploadDataDir + File.separator + currentUserId + identifier + File.separator + i + UPLOAD_FILE_CHUNK_SUFFIX;
if (!FileUtil.exist(new File(tmpPath))) {
String info = StrFormatter.format("第{}个分片没有上传完成,请上传完成后再合并。", i);
log.error(info);
throw new BizException(-1, info);
}
}
// 合并分片
String destZipPath = uploadDataDir + File.separator + currentUserId + File.separator + identifier + File.separator + fileName;
File[] partFiles = FileUtil.ls(uploadDataDir + File.separator + currentUserId + File.separator + identifier);
for (File partFile : partFiles) {
try (BufferedOutputStream outputStream = FileUtil.getOutputStream(destZipPath);
BufferedInputStream inputStream = FileUtil.getInputStream(partFile)) {
IoUtil.copy(inputStream, outputStream);
} catch (Exception e) {
FileUtil.del(destZipPath);
String info = "文件合并失败";
log.error(info, e);
throw new BizException(-1, info);
}
}
Arrays.stream(partFiles).forEach(e -> FileUtil.del(e.getAbsolutePath()));
return destZipPath;
}
}

View File

@ -198,7 +198,6 @@ public class EdPrjServiceImpl extends ServiceImpl<EdFileInfoMapper, EdFileInfo>
/**
* 添加子集
*
* @param parentId
* @param folderName
* @return
@ -206,12 +205,13 @@ public class EdPrjServiceImpl extends ServiceImpl<EdFileInfoMapper, EdFileInfo>
@Override
@Transactional(rollbackFor = Exception.class)
public ElectromagneticResult<?> addFolder(String parentId, String folderName) {
return commonService.addFolder(parentId, folderName, true, true);
int id = Integer.parseInt(this.baseMapper.maxPrjId());
String folderId = String.valueOf(id + 1);
return commonService.addFolder(parentId, folderName, true, true, folderId);
}
/**
* 查询所有项目
*
* @return
*/
@Override

View File

@ -13,8 +13,13 @@ spring.servlet.multipart.max-request-size=10MB
pagehelper.helperDialect=mysql
pagehelper.reasonable=false
server.port=12395
file.security.passwd=adknfhkj87654knd
#windows文件存储目录用于测试
data.windows.path=E:/tmp/eleData/
data.linux.path=/szsd/data/eleData
data.windows.path=D:/tmp/eleData/project/
data.linux.path=/szsd/data/eleData/project/
data.upload.windows.tmp.path=D:/tmp/eleData/upload/
data.upload.linux.tmp.path=/szsd/data/eleData/upload/
data.download.windows.tmp.path=D:/tmp/eleData/download/
data.download.linux.tmp.path=/szsd/data/eleData/download/
prj.folder.max.length=6

View File

@ -9,4 +9,6 @@ public interface ElectromagneticConstants {
int FILE_START_VERSION = 100;
String PRJ_PARENT_ID = "0";
String UPLOAD_FILE_CHUNK_SUFFIX = ".part";
}