修改文件下载模式,完善文件删除逻辑,
This commit is contained in:
parent
cb98c405f4
commit
5bb693e2b7
@ -65,8 +65,8 @@ public class FileController extends AbstractController {
|
|||||||
@ApiImplicitParam(name = "ids", value = "ID列表,用下划线分隔", paramType = "path", example = "1_2_3")
|
@ApiImplicitParam(name = "ids", value = "ID列表,用下划线分隔", paramType = "path", example = "1_2_3")
|
||||||
})
|
})
|
||||||
@ApiResponses({@ApiResponse(code = 400, message = "请求失败", response = ErrorResult.class)})
|
@ApiResponses({@ApiResponse(code = 400, message = "请求失败", response = ErrorResult.class)})
|
||||||
@DeleteMapping("removearticlecategory/{isRemoveSource}/{ids}")
|
@DeleteMapping("removefile/{isRemoveSource}/{ids}")
|
||||||
public SuccessResult removeFile(@PathVariable() Integer isRemoveSource, @PathVariable("ids") String ids) throws RemoveException {
|
public synchronized SuccessResult removeFile(@PathVariable() Integer isRemoveSource, @PathVariable("ids") String ids) throws RemoveException {
|
||||||
if (isRemoveSource == 0) {
|
if (isRemoveSource == 0) {
|
||||||
fileService.removeFile(ids);
|
fileService.removeFile(ids);
|
||||||
}
|
}
|
||||||
|
@ -46,6 +46,14 @@ public interface IFileDao {
|
|||||||
*/
|
*/
|
||||||
void deleteFile(Map<String, Object> params) throws RemoveException;
|
void deleteFile(Map<String, Object> params) throws RemoveException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新文件描述
|
||||||
|
*
|
||||||
|
* @param fileParams
|
||||||
|
* @throws SearchException
|
||||||
|
*/
|
||||||
|
void updateFileSummary(Map<String, Object> fileParams) throws SearchException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取文件详情
|
* 获取文件详情
|
||||||
*
|
*
|
||||||
@ -89,4 +97,6 @@ public interface IFileDao {
|
|||||||
* @throws SearchException
|
* @throws SearchException
|
||||||
*/
|
*/
|
||||||
List<FileInfoDTO> listFileByMd5(String fileMd5) throws SearchException;
|
List<FileInfoDTO> listFileByMd5(String fileMd5) throws SearchException;
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -42,6 +42,9 @@ import javax.servlet.http.HttpServletRequest;
|
|||||||
import javax.servlet.http.HttpServletResponse;
|
import javax.servlet.http.HttpServletResponse;
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
import java.net.URLEncoder;
|
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.security.MessageDigest;
|
||||||
import java.util.*;
|
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 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'};
|
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
|
@Autowired
|
||||||
private FileProperties fileProperties;
|
private FileProperties fileProperties;
|
||||||
@ -92,23 +103,72 @@ public class FileServiceImpl extends AbstractService implements IFileService {
|
|||||||
public void deleteFile(String ids) throws RemoveException {
|
public void deleteFile(String ids) throws RemoveException {
|
||||||
Map<String, Object> params = getHashMap(2);
|
Map<String, Object> params = getHashMap(2);
|
||||||
params.put("fileIds", Arrays.asList(ids.split("_")));
|
params.put("fileIds", Arrays.asList(ids.split("_")));
|
||||||
|
Map<String, Object> fileParams = getHashMap(4);
|
||||||
List<FileInfoWithPathDTO> fileInfoWithPathDTOs = fileDao.listFileInfoWithPath(params);
|
List<FileInfoWithPathDTO> fileInfoWithPathDTOs = fileDao.listFileInfoWithPath(params);
|
||||||
// 删除文件
|
// 删除文件
|
||||||
for (FileInfoWithPathDTO fileInfoWithPathDTO : fileInfoWithPathDTOs) {
|
for (FileInfoWithPathDTO fileInfoWithPathDTO : fileInfoWithPathDTOs) {
|
||||||
File file = new File(fileInfoWithPathDTO.getFilePath());
|
// 如果文件描述为空,可以直接删除源文件
|
||||||
if (file.exists()) {
|
if (StringUtils.isBlank(fileInfoWithPathDTO.getFileSummary())) {
|
||||||
boolean isDelete = file.delete();
|
deleteSourceFile(fileInfoWithPathDTO.getFilePath());
|
||||||
if (isDelete) {
|
continue;
|
||||||
LOG.debug("文件删除成功");
|
|
||||||
} else {
|
|
||||||
LOG.debug("文件删除失败");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
// 文件描述不为空时,需要判断是否删除的是源文件,源文件在一个系统中只保留一份
|
||||||
|
// 如果是引用文件的数据,不删除源文件
|
||||||
|
if (fileInfoWithPathDTO.getFileSummary().startsWith(FILE_REF_PREFIX)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// 如果不是MD5源文件,略过
|
||||||
|
if (!fileInfoWithPathDTO.getFileSummary().startsWith(FILE_MD5_PREFIX)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// 如果删除的是源文件,需要查询系统中是否还存在引用的数据
|
||||||
|
List<FileInfoDTO> 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<String> 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);
|
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
|
@Override
|
||||||
public SuccessResultData<String> uploadSingle(MultipartFile uploadFile, UploadTypeEnum uploadTypeEnum, Map<String, Object> params) throws SystemException {
|
public SuccessResultData<String> uploadSingle(MultipartFile uploadFile, UploadTypeEnum uploadTypeEnum, Map<String, Object> params) throws SystemException {
|
||||||
uploadFile(null, uploadFile, uploadTypeEnum, params);
|
uploadFile(null, uploadFile, uploadTypeEnum, params);
|
||||||
@ -160,7 +220,7 @@ public class FileServiceImpl extends AbstractService implements IFileService {
|
|||||||
throw new SaveException("文件上传失败");
|
throw new SaveException("文件上传失败");
|
||||||
}
|
}
|
||||||
// 获取MD5相同的文件
|
// 获取MD5相同的文件
|
||||||
List<FileInfoDTO> fileInfoDTOs = fileDao.listFileByMd5("MD5:" + fileMd5);
|
List<FileInfoDTO> fileInfoDTOs = fileDao.listFileByMd5(FILE_MD5_PREFIX + fileMd5);
|
||||||
if (fileInfoDTOs.size() > 0) {
|
if (fileInfoDTOs.size() > 0) {
|
||||||
// 删除新增的文件
|
// 删除新增的文件
|
||||||
File uploadedFile = new File(uploadPath + File.separator + uploadFileName);
|
File uploadedFile = new File(uploadPath + File.separator + uploadFileName);
|
||||||
@ -266,6 +326,68 @@ public class FileServiceImpl extends AbstractService implements IFileService {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void downLoadFile(HttpServletRequest request, HttpServletResponse response, Map<String, Object> params, boolean canRange) throws FileException {
|
public void downLoadFile(HttpServletRequest request, HttpServletResponse response, Map<String, Object> 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<String, Object> params, boolean canRange) throws FileException {
|
||||||
FilePO filePO = fileDao.getFile(params);
|
FilePO filePO = fileDao.getFile(params);
|
||||||
if (null == filePO) {
|
if (null == filePO) {
|
||||||
throw new SearchException("文件获取失败");
|
throw new SearchException("文件获取失败");
|
||||||
|
@ -87,7 +87,7 @@
|
|||||||
UPDATE
|
UPDATE
|
||||||
sys_file
|
sys_file
|
||||||
SET
|
SET
|
||||||
is_delete = 0,
|
is_delete = 1,
|
||||||
modifier = #{modifier},
|
modifier = #{modifier},
|
||||||
gmt_modified = #{gmtModified}
|
gmt_modified = #{gmtModified}
|
||||||
WHERE
|
WHERE
|
||||||
@ -108,6 +108,24 @@
|
|||||||
</foreach>
|
</foreach>
|
||||||
</delete>
|
</delete>
|
||||||
|
|
||||||
|
<!-- 更新文件描述 -->
|
||||||
|
<update id="updateFileSummary" parameterType="map">
|
||||||
|
UPDATE
|
||||||
|
sys_file
|
||||||
|
SET
|
||||||
|
file_summary = #{fileSummary}
|
||||||
|
WHERE
|
||||||
|
<if test="fileId != null and fileId != ''">
|
||||||
|
file_id = #{fileId}
|
||||||
|
</if>
|
||||||
|
<if test="fileIds != null and fileIds.size > 0">
|
||||||
|
file_id IN
|
||||||
|
<foreach collection="fileIds" index="index" open="(" separator="," close=")">
|
||||||
|
#{fileIds[${index}]}
|
||||||
|
</foreach>
|
||||||
|
</if>
|
||||||
|
</update>
|
||||||
|
|
||||||
<!-- 获取文件 -->
|
<!-- 获取文件 -->
|
||||||
<select id="getFile" parameterType="map" resultMap="filePO">
|
<select id="getFile" parameterType="map" resultMap="filePO">
|
||||||
SELECT
|
SELECT
|
||||||
|
@ -172,6 +172,29 @@
|
|||||||
return rowData;
|
return rowData;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{field: 'fileSummary', width: 350, title: '文件描述', align:'center',
|
||||||
|
templet: function(row) {
|
||||||
|
var rowData = row[this.field];
|
||||||
|
if(typeof(rowData) === 'undefined' || rowData == null || rowData == '') {
|
||||||
|
return '-';
|
||||||
|
}
|
||||||
|
return rowData;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{field: 'isSourceFile', width: 100, title: '保存类型', align:'center',
|
||||||
|
templet: function(row) {
|
||||||
|
if(!row.fileSummary) {
|
||||||
|
return '无';
|
||||||
|
}
|
||||||
|
if(row.fileSummary.indexOf('MD5:') == 0) {
|
||||||
|
return '源文件';
|
||||||
|
}
|
||||||
|
if(row.fileSummary.indexOf('REF:') == 0) {
|
||||||
|
return '引用';
|
||||||
|
}
|
||||||
|
return '错误';
|
||||||
|
}
|
||||||
|
},
|
||||||
]
|
]
|
||||||
],
|
],
|
||||||
page: true,
|
page: true,
|
||||||
@ -216,7 +239,7 @@
|
|||||||
// 删除
|
// 删除
|
||||||
function removeFile(isRemoveSource, ids) {
|
function removeFile(isRemoveSource, ids) {
|
||||||
var layIndex;
|
var layIndex;
|
||||||
top.restAjax.delete(top.restAjax.path('api/file/removearticlecategory/{isRemoveSource}/{ids}', [isRemoveSource, ids]), {}, null, function (code, data) {
|
top.restAjax.delete(top.restAjax.path('api/file/removefile/{isRemoveSource}/{ids}', [isRemoveSource, ids]), {}, null, function (code, data) {
|
||||||
top.dialog.msg(top.dataMessage.deleteSuccess, {time: 1000});
|
top.dialog.msg(top.dataMessage.deleteSuccess, {time: 1000});
|
||||||
reloadTable();
|
reloadTable();
|
||||||
}, function (code, data) {
|
}, function (code, data) {
|
||||||
|
Loading…
Reference in New Issue
Block a user