diff --git a/.idea/.gitignore b/.idea/.gitignore index 26d3352..0ee0a9a 100644 --- a/.idea/.gitignore +++ b/.idea/.gitignore @@ -1,3 +1,4 @@ # Default ignored files /shelf/ /workspace.xml +CopilotChatHistory.xml diff --git a/src/main/java/com/imyeyu/server/modules/system/controller/FileController.java b/src/main/java/com/imyeyu/server/modules/system/controller/FileController.java index 0a3ebbd..ee818b3 100644 --- a/src/main/java/com/imyeyu/server/modules/system/controller/FileController.java +++ b/src/main/java/com/imyeyu/server/modules/system/controller/FileController.java @@ -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.util.ResourceHandler; import com.imyeyu.server.modules.system.vo.ListFileToRequest; +import com.imyeyu.spring.TimiSpring; import com.imyeyu.spring.annotation.AOPLog; import com.imyeyu.spring.annotation.IgnoreGlobalReturn; import com.imyeyu.spring.annotation.RequestRateLimit; +import com.imyeyu.spring.bean.RequestRange; import com.imyeyu.utils.OS; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; +import lombok.Cleanup; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import net.coobird.thumbnailator.Thumbnails; @@ -47,6 +50,7 @@ import javax.imageio.ImageIO; import java.awt.image.BufferedImage; import java.io.ByteArrayOutputStream; import java.io.File; +import java.io.RandomAccessFile; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; @@ -195,6 +199,7 @@ public class FileController implements TimiJava, OS.FileSystem { resp.setStatus(HttpServletResponse.SC_NOT_FOUND); return; } + resp.setContentLengthLong(Files.size(filePath)); String mimeType = new Tika().detect(filePath); if (TimiJava.isNotEmpty(mimeType)) { resp.setContentType(mimeType); @@ -452,27 +457,41 @@ public class FileController implements TimiJava, OS.FileSystem { /** * 下载文件 * - * @param req 请求对象 - * @param resp 回调对象 + * @param resp 返回对象 */ @AOPLog @RequestRateLimit @IgnoreGlobalReturn @RequestMapping("/download/**") - public void download(HttpServletRequest req, HttpServletResponse resp) { + public void download(HttpServletResponse resp) { 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; File file = new File(path); service.checkAccessPermission(file.getAbsolutePath()); + String mimeType = new Tika().detect(file); resp.setContentType(mimeType); resp.setHeader("Content-Disposition", Network.getFileDownloadHeader(file.getName())); - resp.setHeader("Content-Range", String.valueOf(file.length() - 1)); resp.setHeader("Accept-Ranges", "bytes"); - resp.setContentLengthLong(file.length()); - IO.toOutputStream(resp.getOutputStream(), file); - resp.flushBuffer(); + + RequestRange range = TimiSpring.requestRange(file.length()); + if (range == null) { + // 完整文件 + resp.setContentLengthLong(file.length()); + resp.setStatus(HttpServletResponse.SC_OK); + IO.toOutputStream(resp.getOutputStream(), file); + } 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) { log.error("download error", e); resp.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);