增加文件V2直接下载功能

This commit is contained in:
wanggeng 2022-08-08 16:42:38 +08:00
parent 40d103963b
commit 596fe32678
10 changed files with 171 additions and 83 deletions

View File

@ -14,6 +14,9 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* @ClassName: FileAppCenterController
* @Description: APP文件远程
@ -97,7 +100,17 @@ public class FileCenterAppController extends DefaultBaseController {
return new SuccessResultData<>(fileCenterService.uploadSingleByUserId(creator, audio, UploadTypeEnum.AUDIO));
}
@ApiOperation(value = "文件下载", notes = "文件下载(用于直接下载)接口")
@ApiImplicitParams({
@ApiImplicitParam(name = "fileId", value = "文件ID", paramType = "path")
})
@ApiResponses({@ApiResponse(code = 400, message = "请求失败", response = ErrorResult.class)})
@GetMapping("download/{fileId}")
public void download(HttpServletRequest request,
HttpServletResponse response,
@PathVariable("fileId") String fileId) {
fileCenterService.download(request, response, fileId);
}
/**
* 校验Key与Secret

View File

@ -35,7 +35,7 @@ public class FileRouteController {
return modelAndView;
}
@ApiOperation(value = "文件下载", notes = "文件下载接口")
@ApiOperation(value = "文件重定向下载(浏览器)", notes = "文件重定向下载(适用于浏览器访问图片)接口")
@ApiImplicitParams({
@ApiImplicitParam(name = "isOpen", value = "是否打开,true和false", paramType = "path"),
@ApiImplicitParam(name = "fileId", value = "文件ID", paramType = "path")
@ -46,7 +46,19 @@ public class FileRouteController {
HttpServletResponse response,
@PathVariable("isOpen") Boolean isOpen,
@PathVariable("fileId") String fileId) {
fileService.download(request, response, isOpen, fileId);
fileService.downloadRedirect(request, response, isOpen, fileId);
}
@ApiOperation(value = "文件下载", notes = "文件下载(用于直接下载)接口")
@ApiImplicitParams({
@ApiImplicitParam(name = "fileId", value = "文件ID", paramType = "path")
})
@ApiResponses({@ApiResponse(code = 400, message = "请求失败", response = ErrorResult.class)})
@GetMapping("download/{fileId}")
public void download(HttpServletRequest request,
HttpServletResponse response,
@PathVariable("fileId") String fileId) {
fileService.download(request, response, fileId);
}
}

View File

@ -4,6 +4,9 @@ import ink.wgink.module.file.enums.UploadTypeEnum;
import ink.wgink.module.file.pojo.dtos.v2.FileUploadSuccessDTO;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* @ClassName: IFileCenterService
* @Description: 文件中心
@ -23,5 +26,13 @@ public interface IFileCenterService {
*/
FileUploadSuccessDTO uploadSingleByUserId(String userId, MultipartFile uploadFile, UploadTypeEnum uploadTypeEnum);
/**
* 文件下载
*
* @param request
* @param response
* @param fileId
*/
void download(HttpServletRequest request, HttpServletResponse response, String fileId);
}

View File

@ -1,18 +1,12 @@
package ink.wgink.module.file.service.filecenter.impl;
import ink.wgink.common.base.DefaultBaseService;
import ink.wgink.exceptions.FileException;
import ink.wgink.exceptions.SearchException;
import ink.wgink.module.file.enums.UploadTypeEnum;
import ink.wgink.module.file.pojo.dtos.v2.FileUploadSuccessDTO;
import ink.wgink.module.file.pojo.vos.v2.FileSaveVO;
import ink.wgink.module.file.service.filecenter.IFileCenterService;
import ink.wgink.module.file.service.filedownload.IFileDownloadService;
import ink.wgink.module.file.service.v2.IFileService;
import ink.wgink.pojo.pos.FilePO;
import ink.wgink.util.request.StaticResourceRequestUtil;
import org.apache.catalina.connector.ClientAbortException;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
@ -21,12 +15,6 @@ import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.RandomAccessFile;
import java.net.URLEncoder;
import java.nio.channels.Channels;
import java.nio.channels.FileChannel;
import java.nio.channels.WritableByteChannel;
/**
* @ClassName: FileCenterServiceImpl
@ -40,8 +28,6 @@ public class FileCenterServiceImpl extends DefaultBaseService implements IFileCe
@Autowired
private IFileService fileService;
@Autowired
private IFileDownloadService fileDownloadService;
@Override
public FileUploadSuccessDTO uploadSingleByUserId(String userId, MultipartFile uploadFile, UploadTypeEnum uploadTypeEnum) {
@ -49,50 +35,9 @@ public class FileCenterServiceImpl extends DefaultBaseService implements IFileCe
return fileService.saveFile(fileSaveVO);
}
private void download(HttpServletRequest request, HttpServletResponse response, String fileId) {
FilePO filePO = fileService.getPO(fileId);
try (
RandomAccessFile randomAccessFile = new RandomAccessFile(filePO.getFilePath(), "r");
FileChannel fileChannel = randomAccessFile.getChannel();
OutputStream outputStream = response.getOutputStream();
WritableByteChannel writableByteChannel = Channels.newChannel(outputStream);
) {
response.setHeader("Content-Length", filePO.getFileSize().toString());
response.setContentType(StaticResourceRequestUtil.getContentType(filePO.getFileType()));
response.setHeader("Content-Disposition", "inline;fileName=" + URLEncoder.encode(filePO.getFileName(), "UTF-8"));
String rangeString = request.getHeader("Range");
long contentLength = filePO.getFileSize();
long startRange = 0;
long endRange = contentLength - 1;
if (!StringUtils.isBlank(rangeString)) {
response.setContentType("multipart/byteranges");
String[] rangeArray = rangeString.substring(rangeString.indexOf("=") + 1).split("-");
startRange = Long.parseLong(rangeArray[0]);
if (rangeArray.length > 1) {
endRange = Long.parseLong(rangeArray[1]);
}
response.setHeader("Content-Length", String.valueOf(endRange - startRange + 1));
response.setHeader("Content-Range", String.format("bytes %d-%d/%d", startRange, endRange, contentLength));
response.setHeader("Accept-Ranges", "bytes");
response.setHeader("Etag", fileId);
response.setStatus(206);
randomAccessFile.seek(startRange);
}
LOG.debug("startRange: {}, endRange: {}", startRange, endRange);
long totalOutputLength = endRange - startRange + 1;
fileChannel.transferTo(startRange, totalOutputLength, writableByteChannel);
outputStream.flush();
if (endRange >= contentLength - 1) {
fileDownloadService.handle(request, false, fileId);
}
} catch (Exception e) {
e.printStackTrace();
if (e instanceof ClientAbortException) {
LOG.debug("客户端断开连接");
} else {
throw new FileException("文件输出异常", e);
}
}
@Override
public void download(HttpServletRequest request, HttpServletResponse response, String fileId) {
fileService.download(request, response, fileId);
}
/**

View File

@ -3,9 +3,11 @@ package ink.wgink.module.file.service.filedownload;
import ink.wgink.module.file.pojo.dtos.filedownload.FileDownloadDTO;
import ink.wgink.module.file.pojo.vos.filedownload.FileDownloadVO;
import ink.wgink.pojo.ListPage;
import ink.wgink.pojo.pos.FilePO;
import ink.wgink.pojo.result.SuccessResultList;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.List;
import java.util.Map;
@ -68,12 +70,22 @@ public interface IFileDownloadService {
Integer countByFileId(String fileId);
/**
* 下载
* 文件下载
*
* @param httpServletRequest
* @param isOpen 是否打开
* @param fileId
* @param request
* @param response
* @param filePO
*/
void handle(HttpServletRequest httpServletRequest, boolean isOpen, String fileId);
void download(HttpServletRequest request, HttpServletResponse response, FilePO filePO);
/**
* 文件重定向下载
*
* @param request
* @param response
* @param isOpen
* @param filePO
*/
void downloadRedirect(HttpServletRequest request, HttpServletResponse response, boolean isOpen, FilePO filePO);
}

View File

@ -3,22 +3,36 @@ package ink.wgink.module.file.service.filedownload.impl;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import ink.wgink.common.base.DefaultBaseService;
import ink.wgink.exceptions.FileException;
import ink.wgink.module.file.dao.filedownload.IFileDownloadDao;
import ink.wgink.module.file.manager.FilesManager;
import ink.wgink.module.file.pojo.dtos.filedownload.FileDownloadDTO;
import ink.wgink.module.file.pojo.vos.filedownload.FileDownloadVO;
import ink.wgink.module.file.service.filedownload.IFileDownloadService;
import ink.wgink.pojo.ListPage;
import ink.wgink.pojo.pos.FilePO;
import ink.wgink.pojo.result.SuccessResultList;
import ink.wgink.util.UUIDUtil;
import ink.wgink.util.date.DateUtil;
import ink.wgink.util.map.HashMapUtil;
import ink.wgink.util.request.RequestUtil;
import ink.wgink.util.request.StaticResourceRequestUtil;
import ink.wgink.util.thread.CachedThreadPoolUtil;
import org.apache.catalina.connector.ClientAbortException;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
import java.io.RandomAccessFile;
import java.net.URLEncoder;
import java.nio.channels.Channels;
import java.nio.channels.FileChannel;
import java.nio.channels.WritableByteChannel;
import java.util.List;
import java.util.Map;
@ -77,8 +91,74 @@ public class FileDownloadServiceImpl extends DefaultBaseService implements IFile
return fileDownloadDao.count(params);
}
@Override
public void handle(HttpServletRequest httpServletRequest, boolean isOpen, String fileId) {
public void download(HttpServletRequest request, HttpServletResponse response, FilePO filePO) {
String fileId = filePO.getFileId();
try (
RandomAccessFile randomAccessFile = new RandomAccessFile(filePO.getFilePath(), "r");
FileChannel fileChannel = randomAccessFile.getChannel();
OutputStream outputStream = response.getOutputStream();
WritableByteChannel writableByteChannel = Channels.newChannel(outputStream);
) {
response.setHeader("Content-Length", filePO.getFileSize().toString());
response.setContentType(StaticResourceRequestUtil.getContentType(filePO.getFileType()));
response.setHeader("Content-Disposition", "inline;fileName=" + URLEncoder.encode(filePO.getFileName(), "UTF-8"));
String rangeString = request.getHeader("Range");
long contentLength = filePO.getFileSize();
long startRange = 0;
long endRange = contentLength - 1;
if (!StringUtils.isBlank(rangeString)) {
response.setContentType("multipart/byteranges");
String[] rangeArray = rangeString.substring(rangeString.indexOf("=") + 1).split("-");
startRange = Long.parseLong(rangeArray[0]);
if (rangeArray.length > 1) {
endRange = Long.parseLong(rangeArray[1]);
}
response.setHeader("Content-Length", String.valueOf(endRange - startRange + 1));
response.setHeader("Content-Range", String.format("bytes %d-%d/%d", startRange, endRange, contentLength));
response.setHeader("Accept-Ranges", "bytes");
response.setHeader("Etag", fileId);
response.setStatus(206);
randomAccessFile.seek(startRange);
}
LOG.debug("startRange: {}, endRange: {}", startRange, endRange);
long totalOutputLength = endRange - startRange + 1;
fileChannel.transferTo(startRange, totalOutputLength, writableByteChannel);
outputStream.flush();
if (endRange >= contentLength - 1) {
handle(request, false, fileId);
}
} catch (Exception e) {
e.printStackTrace();
if (e instanceof ClientAbortException) {
LOG.debug("客户端断开连接");
} else {
throw new FileException("文件输出异常", e);
}
}
}
@Override
public void downloadRedirect(HttpServletRequest request, HttpServletResponse response, boolean isOpen, FilePO filePO) {
if (filePO == null) {
throw new FileException("查询失败");
}
File file = new File(filePO.getFilePath());
if (!file.exists()) {
throw new FileException("文件不存在");
}
String fileId = filePO.getFileId();
try {
String code = FilesManager.getInstance().generateCode(fileId, filePO.getFileName());
response.sendRedirect(String.format("%s/%s?file=%s&code=%s&open=%d", request.getContextPath(), filePO.getFileUrl(), fileId, code, isOpen ? 1 : 0));
handle(request, isOpen, fileId);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
private void handle(HttpServletRequest httpServletRequest, boolean isOpen, String fileId) {
String requestIp = RequestUtil.getRequestIp(httpServletRequest);
if (!isOpen && !StringUtils.isBlank(requestIp)) {
// 记录下载历史
@ -88,5 +168,4 @@ public class FileDownloadServiceImpl extends DefaultBaseService implements IFile
}
}
}

View File

@ -224,7 +224,7 @@ public class MinIoFileServiceImpl extends DefaultBaseService implements IMinIoFi
} else {
inputStream = minioClient.getObject(GetObjectArgs.builder().bucket(filePO.getFileUrl()).object(objectName).offset(0L).length(contentLength).build());
}
if (endRange == contentLength) {
if (endRange >= (contentLength - 1)) {
isDownloadComplete = true;
}
byte[] readBuf = new byte[IFileService.INPUT_STREAM_SIZE];

View File

@ -129,14 +129,24 @@ public interface IFileService {
FilePO getPO(String fileId);
/**
* 下载
* 重定向下载
*
* @param request
* @param response
* @param isOpen 是否打开
* @param fileId 文件ID
*/
void download(HttpServletRequest request, HttpServletResponse response, boolean isOpen, String fileId);
void downloadRedirect(HttpServletRequest request, HttpServletResponse response, boolean isOpen, String fileId);
/**
* 下载
*
* @param request
* @param response
* @param fileId
*/
void download(HttpServletRequest request, HttpServletResponse response, String fileId);
/**
* 文件列表

View File

@ -9,7 +9,6 @@ import ink.wgink.exceptions.SearchException;
import ink.wgink.module.file.components.FileComponent;
import ink.wgink.module.file.dao.IFileDao;
import ink.wgink.module.file.enums.UploadTypeEnum;
import ink.wgink.module.file.manager.FilesManager;
import ink.wgink.module.file.pojo.dtos.FileInfoDTO;
import ink.wgink.module.file.pojo.dtos.v2.FileUploadSuccessDTO;
import ink.wgink.module.file.pojo.vos.v2.FileSaveVO;
@ -169,7 +168,7 @@ public class FileServiceImpl extends DefaultBaseService implements IFileService
}
@Override
public void download(HttpServletRequest request, HttpServletResponse response, boolean isOpen, String fileId) {
public void downloadRedirect(HttpServletRequest request, HttpServletResponse response, boolean isOpen, String fileId) {
FilePO filePO = getPO(fileId);
if (filePO == null) {
throw new FileException("查询失败");
@ -178,13 +177,20 @@ public class FileServiceImpl extends DefaultBaseService implements IFileService
if (!file.exists()) {
throw new FileException("文件不存在");
}
try {
String code = FilesManager.getInstance().generateCode(fileId, filePO.getFileName());
response.sendRedirect(String.format("%s/%s?file=%s&code=%s&open=%d", request.getContextPath(), filePO.getFileUrl(), fileId, code, isOpen ? 1 : 0));
fileDownloadService.handle(request, isOpen, fileId);
} catch (IOException e) {
throw new RuntimeException(e);
fileDownloadService.downloadRedirect(request, response, isOpen, filePO);
}
@Override
public void download(HttpServletRequest request, HttpServletResponse response, String fileId) {
FilePO filePO = getPO(fileId);
if (filePO == null) {
throw new FileException("查询失败");
}
File file = new File(filePO.getFilePath());
if (!file.exists()) {
throw new FileException("文件不存在");
}
fileDownloadService.download(request, response, filePO);
}
@Override

View File

@ -181,7 +181,7 @@
yes: function (index) {
top.dialog.close(index);
var layIndex;
top.restAjax.delete(top.restAjax.path('api/file-remote/remove/{ids}', [ids]), {}, null, function (code, data) {
top.restAjax.delete(top.restAjax.path('api/file-client/remove/{ids}', [ids]), {}, null, function (code, data) {
top.dialog.msg(top.dataMessage.deleteSuccess, {time: 1000});
reloadTable();
}, function (code, data) {
@ -208,7 +208,7 @@
area: ['100%', '100%'],
shadeClose: true,
anim: 2,
content: top.restAjax.path('route/file-remote/save', []),
content: top.restAjax.path('route/file-client/save', []),
end: function() {
reloadTable();
}
@ -226,7 +226,7 @@
area: ['100%', '100%'],
shadeClose: true,
anim: 2,
content: top.restAjax.path('route/file-remote/update?fileRemoteId={fileRemoteId}', [checkDatas[0].fileRemoteId]),
content: top.restAjax.path('route/file-client/update?fileClientId={fileClientId}', [checkDatas[0].fileClientId]),
end: function() {
reloadTable();
}
@ -241,7 +241,7 @@
if(i > 1) {
ids += '_';
}
ids += item['fileRemoteId'];
ids += item['fileClientId'];
}
removeData(ids);
}