support RequestRange for system file download
This commit is contained in:
1
.idea/.gitignore
generated
vendored
1
.idea/.gitignore
generated
vendored
@ -1,3 +1,4 @@
|
|||||||
# Default ignored files
|
# Default ignored files
|
||||||
/shelf/
|
/shelf/
|
||||||
/workspace.xml
|
/workspace.xml
|
||||||
|
CopilotChatHistory.xml
|
||||||
|
|||||||
@ -23,12 +23,15 @@ import com.imyeyu.server.modules.system.task.async.FileUnZipAsyncTask;
|
|||||||
import com.imyeyu.server.modules.system.task.async.FileZipAsyncTask;
|
import com.imyeyu.server.modules.system.task.async.FileZipAsyncTask;
|
||||||
import com.imyeyu.server.modules.system.util.ResourceHandler;
|
import com.imyeyu.server.modules.system.util.ResourceHandler;
|
||||||
import com.imyeyu.server.modules.system.vo.ListFileToRequest;
|
import com.imyeyu.server.modules.system.vo.ListFileToRequest;
|
||||||
|
import com.imyeyu.spring.TimiSpring;
|
||||||
import com.imyeyu.spring.annotation.AOPLog;
|
import com.imyeyu.spring.annotation.AOPLog;
|
||||||
import com.imyeyu.spring.annotation.IgnoreGlobalReturn;
|
import com.imyeyu.spring.annotation.IgnoreGlobalReturn;
|
||||||
import com.imyeyu.spring.annotation.RequestRateLimit;
|
import com.imyeyu.spring.annotation.RequestRateLimit;
|
||||||
|
import com.imyeyu.spring.bean.RequestRange;
|
||||||
import com.imyeyu.utils.OS;
|
import com.imyeyu.utils.OS;
|
||||||
import jakarta.servlet.http.HttpServletRequest;
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
import jakarta.servlet.http.HttpServletResponse;
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
|
import lombok.Cleanup;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import net.coobird.thumbnailator.Thumbnails;
|
import net.coobird.thumbnailator.Thumbnails;
|
||||||
@ -47,6 +50,7 @@ import javax.imageio.ImageIO;
|
|||||||
import java.awt.image.BufferedImage;
|
import java.awt.image.BufferedImage;
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
import java.io.RandomAccessFile;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
@ -195,6 +199,7 @@ public class FileController implements TimiJava, OS.FileSystem {
|
|||||||
resp.setStatus(HttpServletResponse.SC_NOT_FOUND);
|
resp.setStatus(HttpServletResponse.SC_NOT_FOUND);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
resp.setContentLengthLong(Files.size(filePath));
|
||||||
String mimeType = new Tika().detect(filePath);
|
String mimeType = new Tika().detect(filePath);
|
||||||
if (TimiJava.isNotEmpty(mimeType)) {
|
if (TimiJava.isNotEmpty(mimeType)) {
|
||||||
resp.setContentType(mimeType);
|
resp.setContentType(mimeType);
|
||||||
@ -452,27 +457,41 @@ public class FileController implements TimiJava, OS.FileSystem {
|
|||||||
/**
|
/**
|
||||||
* 下载文件
|
* 下载文件
|
||||||
*
|
*
|
||||||
* @param req 请求对象
|
* @param resp 返回对象
|
||||||
* @param resp 回调对象
|
|
||||||
*/
|
*/
|
||||||
@AOPLog
|
@AOPLog
|
||||||
@RequestRateLimit
|
@RequestRateLimit
|
||||||
@IgnoreGlobalReturn
|
@IgnoreGlobalReturn
|
||||||
@RequestMapping("/download/**")
|
@RequestMapping("/download/**")
|
||||||
public void download(HttpServletRequest req, HttpServletResponse resp) {
|
public void download(HttpServletResponse resp) {
|
||||||
try {
|
try {
|
||||||
String path = req.getServletPath().substring("/system/file/download".length());
|
String path = TimiSpring.cutURIStartAt("/system/file/download");
|
||||||
path = settingService.getAsString(SettingKey.SYSTEM_FILE_BASE) + path;
|
path = settingService.getAsString(SettingKey.SYSTEM_FILE_BASE) + path;
|
||||||
File file = new File(path);
|
File file = new File(path);
|
||||||
service.checkAccessPermission(file.getAbsolutePath());
|
service.checkAccessPermission(file.getAbsolutePath());
|
||||||
|
|
||||||
String mimeType = new Tika().detect(file);
|
String mimeType = new Tika().detect(file);
|
||||||
resp.setContentType(mimeType);
|
resp.setContentType(mimeType);
|
||||||
resp.setHeader("Content-Disposition", Network.getFileDownloadHeader(file.getName()));
|
resp.setHeader("Content-Disposition", Network.getFileDownloadHeader(file.getName()));
|
||||||
resp.setHeader("Content-Range", String.valueOf(file.length() - 1));
|
|
||||||
resp.setHeader("Accept-Ranges", "bytes");
|
resp.setHeader("Accept-Ranges", "bytes");
|
||||||
|
|
||||||
|
RequestRange range = TimiSpring.requestRange(file.length());
|
||||||
|
if (range == null) {
|
||||||
|
// 完整文件
|
||||||
resp.setContentLengthLong(file.length());
|
resp.setContentLengthLong(file.length());
|
||||||
|
resp.setStatus(HttpServletResponse.SC_OK);
|
||||||
IO.toOutputStream(resp.getOutputStream(), file);
|
IO.toOutputStream(resp.getOutputStream(), file);
|
||||||
resp.flushBuffer();
|
} else {
|
||||||
|
// 分片文件
|
||||||
|
resp.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT);
|
||||||
|
resp.setHeader("Content-Range", "bytes %s-%s/%s".formatted(range.getStart(), range.getEnd(), file.length()));
|
||||||
|
resp.setContentLengthLong(range.getLength());
|
||||||
|
|
||||||
|
@Cleanup
|
||||||
|
RandomAccessFile raf = new RandomAccessFile(file, "r");
|
||||||
|
raf.seek(range.getStart());
|
||||||
|
IO.toOutputStream(resp.getOutputStream(), raf, range.getStart(), range.getLength());
|
||||||
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("download error", e);
|
log.error("download error", e);
|
||||||
resp.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
|
resp.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
|
||||||
|
|||||||
Reference in New Issue
Block a user