diff --git a/cloud-common-plugin/src/main/java/com/cm/common/plugin/controller/apis/file/FileController.java b/cloud-common-plugin/src/main/java/com/cm/common/plugin/controller/apis/file/FileController.java index 48fb08a..f5f91f6 100644 --- a/cloud-common-plugin/src/main/java/com/cm/common/plugin/controller/apis/file/FileController.java +++ b/cloud-common-plugin/src/main/java/com/cm/common/plugin/controller/apis/file/FileController.java @@ -65,8 +65,8 @@ public class FileController extends AbstractController { @ApiImplicitParam(name = "ids", value = "ID列表,用下划线分隔", paramType = "path", example = "1_2_3") }) @ApiResponses({@ApiResponse(code = 400, message = "请求失败", response = ErrorResult.class)}) - @DeleteMapping("removearticlecategory/{isRemoveSource}/{ids}") - public SuccessResult removeFile(@PathVariable() Integer isRemoveSource, @PathVariable("ids") String ids) throws RemoveException { + @DeleteMapping("removefile/{isRemoveSource}/{ids}") + public synchronized SuccessResult removeFile(@PathVariable() Integer isRemoveSource, @PathVariable("ids") String ids) throws RemoveException { if (isRemoveSource == 0) { fileService.removeFile(ids); } diff --git a/cloud-common-plugin/src/main/java/com/cm/common/plugin/dao/file/IFileDao.java b/cloud-common-plugin/src/main/java/com/cm/common/plugin/dao/file/IFileDao.java index 47a690e..a275e62 100644 --- a/cloud-common-plugin/src/main/java/com/cm/common/plugin/dao/file/IFileDao.java +++ b/cloud-common-plugin/src/main/java/com/cm/common/plugin/dao/file/IFileDao.java @@ -46,6 +46,14 @@ public interface IFileDao { */ void deleteFile(Map params) throws RemoveException; + /** + * 更新文件描述 + * + * @param fileParams + * @throws SearchException + */ + void updateFileSummary(Map fileParams) throws SearchException; + /** * 获取文件详情 * @@ -89,4 +97,6 @@ public interface IFileDao { * @throws SearchException */ List listFileByMd5(String fileMd5) throws SearchException; + + } diff --git a/cloud-common-plugin/src/main/java/com/cm/common/plugin/service/file/impl/FileServiceImpl.java b/cloud-common-plugin/src/main/java/com/cm/common/plugin/service/file/impl/FileServiceImpl.java index 7f8b7c0..32d33ce 100644 --- a/cloud-common-plugin/src/main/java/com/cm/common/plugin/service/file/impl/FileServiceImpl.java +++ b/cloud-common-plugin/src/main/java/com/cm/common/plugin/service/file/impl/FileServiceImpl.java @@ -42,6 +42,9 @@ import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.*; import java.net.URLEncoder; +import java.nio.channels.Channels; +import java.nio.channels.FileChannel; +import java.nio.channels.WritableByteChannel; import java.security.MessageDigest; import java.util.*; @@ -57,6 +60,14 @@ public class FileServiceImpl extends AbstractService implements IFileService { private static final Logger LOG = LoggerFactory.getLogger(FileServiceImpl.class); private static final char[] HEX_CODE = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'}; + /** + * 文件MD5值开头 + */ + public static final String FILE_MD5_PREFIX = "MD5:"; + /** + * 文件引用值开头 + */ + public static final String FILE_REF_PREFIX = "REF:"; @Autowired private FileProperties fileProperties; @@ -92,23 +103,72 @@ public class FileServiceImpl extends AbstractService implements IFileService { public void deleteFile(String ids) throws RemoveException { Map params = getHashMap(2); params.put("fileIds", Arrays.asList(ids.split("_"))); + Map fileParams = getHashMap(4); List fileInfoWithPathDTOs = fileDao.listFileInfoWithPath(params); // 删除文件 for (FileInfoWithPathDTO fileInfoWithPathDTO : fileInfoWithPathDTOs) { - File file = new File(fileInfoWithPathDTO.getFilePath()); - if (file.exists()) { - boolean isDelete = file.delete(); - if (isDelete) { - LOG.debug("文件删除成功"); - } else { - LOG.debug("文件删除失败"); - } + // 如果文件描述为空,可以直接删除源文件 + if (StringUtils.isBlank(fileInfoWithPathDTO.getFileSummary())) { + deleteSourceFile(fileInfoWithPathDTO.getFilePath()); + continue; } + // 文件描述不为空时,需要判断是否删除的是源文件,源文件在一个系统中只保留一份 + // 如果是引用文件的数据,不删除源文件 + if (fileInfoWithPathDTO.getFileSummary().startsWith(FILE_REF_PREFIX)) { + continue; + } + // 如果不是MD5源文件,略过 + if (!fileInfoWithPathDTO.getFileSummary().startsWith(FILE_MD5_PREFIX)) { + continue; + } + // 如果删除的是源文件,需要查询系统中是否还存在引用的数据 + List fileInfoDTOs = fileDao.listFileByMd5(FILE_REF_PREFIX + fileInfoWithPathDTO.getFileId()); + // 如果不存在对源文件引用的数据,则直接删除源文件 + if (fileInfoDTOs.size() == 0) { + deleteSourceFile(fileInfoWithPathDTO.getFilePath()); + continue; + } + fileParams.clear(); + // 如果存在引用数据,取出第一个修改为源文件,并将其他的引用更新为新的源文件ID + FileInfoDTO fileInfoDTO = fileInfoDTOs.get(0); + fileParams.put("fileSummary", fileInfoWithPathDTO.getFileSummary()); + fileParams.put("fileId", fileInfoDTO.getFileId()); + fileDao.updateFileSummary(fileParams); + // 获取其他的ID列表,更新文件引用关系 + List otherFileIds = new ArrayList<>(); + for (int i = 1; i < fileInfoDTOs.size(); i++) { + otherFileIds.add(fileInfoDTOs.get(i).getFileId()); + } + // 如果不存在其它的引用,略过 + if (otherFileIds.isEmpty()) { + continue; + } + fileParams.remove("fileId"); + fileParams.put("fileSummary", FILE_REF_PREFIX + fileInfoDTO.getFileId()); + fileParams.put("fileIds", otherFileIds); + fileDao.updateFileSummary(fileParams); } // 删除记录 fileDao.deleteFile(params); } + /** + * 删除源文件 + * + * @param sourceFilePath 源文件路径 + */ + private void deleteSourceFile(String sourceFilePath) { + File file = new File(sourceFilePath); + if (file.exists()) { + boolean isDelete = file.delete(); + if (isDelete) { + LOG.debug("文件删除成功"); + } else { + LOG.debug("文件删除失败"); + } + } + } + @Override public SuccessResultData uploadSingle(MultipartFile uploadFile, UploadTypeEnum uploadTypeEnum, Map params) throws SystemException { uploadFile(null, uploadFile, uploadTypeEnum, params); @@ -160,7 +220,7 @@ public class FileServiceImpl extends AbstractService implements IFileService { throw new SaveException("文件上传失败"); } // 获取MD5相同的文件 - List fileInfoDTOs = fileDao.listFileByMd5("MD5:" + fileMd5); + List fileInfoDTOs = fileDao.listFileByMd5(FILE_MD5_PREFIX + fileMd5); if (fileInfoDTOs.size() > 0) { // 删除新增的文件 File uploadedFile = new File(uploadPath + File.separator + uploadFileName); @@ -266,6 +326,68 @@ public class FileServiceImpl extends AbstractService implements IFileService { @Override public void downLoadFile(HttpServletRequest request, HttpServletResponse response, Map params, boolean canRange) throws FileException { + FilePO filePO = fileDao.getFile(params); + if (null == filePO) { + throw new SearchException("文件获取失败"); + } + try ( + RandomAccessFile randomAccessFile = new RandomAccessFile(filePO.getFilePath(), "r"); + FileChannel fileChannel = randomAccessFile.getChannel(); + OutputStream outputStream = response.getOutputStream(); + WritableByteChannel writableByteChannel = Channels.newChannel(outputStream); + ) { + boolean isOpen = Boolean.valueOf(params.get("isOpen").toString()); + response.setHeader("Content-Length", filePO.getFileSize()); + response.setContentType(getContentType(filePO.getFileType())); + if (!isOpen) { + response.setHeader("Content-Disposition", "attachment;fileName=" + URLEncoder.encode(filePO.getFileName(), "UTF-8")); + } else { + response.setHeader("Content-Disposition", "inline;fileName=" + URLEncoder.encode(filePO.getFileName(), "UTF-8")); + } + String rangeString = null; + if (canRange && request != null) { + rangeString = request.getHeader("Range"); + LOG.debug("range: {}", rangeString); + } + long contentLength = Long.valueOf(filePO.getFileSize()); + long startRange = 0; + long endRange = contentLength - 1; + if (!StringUtils.isBlank(rangeString)) { + if (!isOpen) { + response.setContentType("multipart/byteranges"); + } + String[] rangeArray = rangeString.substring(rangeString.indexOf("=") + 1).split("-"); + startRange = Long.valueOf(rangeArray[0]); + if (rangeArray.length > 1) { + endRange = Long.valueOf(rangeArray[1]); + } + setRangeHeader(startRange, endRange, response, filePO.getFileId(), contentLength); + randomAccessFile.seek(startRange); + } + LOG.debug("startRange: {}, endRange: {}", startRange, endRange); + long totalOutputLength = endRange - startRange + 1; + fileChannel.transferTo(startRange, totalOutputLength, writableByteChannel); + outputStream.flush(); + } catch (Exception e) { + if (e instanceof ClientAbortException) { + LOG.debug("客户端断开连接"); + } else { + throw new FileException("文件输出异常", e); + } + } + } + + /** + * 同步下载文件 + * + * @param request + * @param response + * @param params + * @param canRange + * @throws FileException + */ + @Deprecated + public void downLoadFileOld(HttpServletRequest request, HttpServletResponse response, Map params, boolean canRange) throws FileException { FilePO filePO = fileDao.getFile(params); if (null == filePO) { throw new SearchException("文件获取失败"); diff --git a/cloud-common-plugin/src/main/resources/mybatis/mapper/file/file-mapper.xml b/cloud-common-plugin/src/main/resources/mybatis/mapper/file/file-mapper.xml index b2ef460..02d21f1 100644 --- a/cloud-common-plugin/src/main/resources/mybatis/mapper/file/file-mapper.xml +++ b/cloud-common-plugin/src/main/resources/mybatis/mapper/file/file-mapper.xml @@ -87,7 +87,7 @@ UPDATE sys_file SET - is_delete = 0, + is_delete = 1, modifier = #{modifier}, gmt_modified = #{gmtModified} WHERE @@ -108,6 +108,24 @@ + + + UPDATE + sys_file + SET + file_summary = #{fileSummary} + WHERE + + file_id = #{fileId} + + + file_id IN + + #{fileIds[${index}]} + + + +