Parcourir la source

1、视频、图片配置浏览器缓存

liyanbo il y a 4 mois
Parent
commit
0d04a5463d

+ 0 - 42
byzs-module-infra/src/main/java/cn/iocoder/byzs/module/infra/controller/admin/file/FileController.java

@@ -115,48 +115,6 @@ public class FileController {
             return;
         }
 
-        // 新增:处理 Range 分片请求(关键)
-//        String rangeHeader = request.getHeader("Range");
-//        if (StrUtil.isNotBlank(rangeHeader) && rangeHeader.startsWith("bytes=")) {
-//            // 解析 Range 格式:bytes=start-end
-//            String range = rangeHeader.substring("bytes=".length());
-//            String[] rangeParts = range.split("-");
-//            long contentLength = content.length;
-//            long start = 0;
-//            long end = contentLength - 1;
-//
-//            // 解析 start
-//            if (StrUtil.isNotBlank(rangeParts[0])) {
-//                start = Long.parseLong(rangeParts[0]);
-//                if (start < 0 || start >= contentLength) {
-//                    response.setStatus(HttpStatus.REQUESTED_RANGE_NOT_SATISFIABLE.value());
-//                    return;
-//                }
-//            }
-//            // 解析 end
-//            if (rangeParts.length > 1 && StrUtil.isNotBlank(rangeParts[1])) {
-//                end = Long.parseLong(rangeParts[1]);
-//                if (end >= contentLength) {
-//                    end = contentLength - 1;
-//                }
-//            }
-//
-//            // 截取分片内容
-//            int startInt = (int) start;
-//            int endInt = (int) end;
-//            byte[] rangeContent = new byte[endInt - startInt + 1];
-//            System.arraycopy(content, startInt, rangeContent, 0, rangeContent.length);
-//
-//            // 设置 206 状态码(部分内容)
-//            response.setStatus(HttpStatus.PARTIAL_CONTENT.value());
-//            // 设置分片响应头
-//            response.setHeader("Content-Range", "bytes " + start + "-" + end + "/" + contentLength);
-//            response.setContentLength(rangeContent.length);
-//
-//            // 输出分片内容(复用 writeAttachment 但调整参数)
-//            writeAttachment(response, path, rangeContent);
-//            return;
-//        }
 
         // 非分片请求:输出完整内容
         writeAttachment(response, path, content);

+ 56 - 41
byzs-module-infra/src/main/java/cn/iocoder/byzs/module/infra/framework/file/core/utils/FileTypeUtils.java

@@ -11,6 +11,9 @@ import org.apache.tika.mime.MimeTypeException;
 import org.apache.tika.mime.MimeTypes;
 
 import java.io.IOException;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.Locale;
 
 /**
  * 文件类型 Utils
@@ -79,56 +82,68 @@ public class FileTypeUtils {
      * @param content  内容(完整或分片)
      */
     public static void writeAttachment(HttpServletResponse response, String filename, byte[] content) throws IOException {
-//        // 1. 基础头设置
-//        String contentType = getMineType(content, filename);
-//
-//        // 2. 设置响应头(根据文件类型)
-////        if (filename.endsWith(".m3u8")) {
-////            contentType = "application/x-mpegURL";
-////        } else if (filename.endsWith(".ts")) {
-////            contentType = "video/MP2T";
-////        }
-//        response.setContentType(contentType);
-//        response.setContentLength(content.length);
-//
-//        // 2. 缓存设置(核心:添加缓存头)
-//        // 视频缓存30天,其他文件缓存7天(可按需调整)
-//        long maxAge = StrUtil.containsIgnoreCase(contentType, "video") ? 30 * 24 * 60 * 60 : 7 * 24 * 60 * 60;
-//        response.setHeader("Cache-Control", "public, max-age=" + maxAge); // 公共缓存,有效期30天
-//        response.setHeader("Pragma", "public"); // 兼容老浏览器
-//
-//        // 3. 文件名处理(视频用 inline 播放,非视频用 attachment 下载)
-//        String disposition = StrUtil.containsIgnoreCase(contentType, "video")
-//                ? "inline;filename=" + HttpUtils.encodeUtf8(filename) // 视频:在线播放
-//                : "attachment;filename=" + HttpUtils.encodeUtf8(filename); // 其他:下载
-//        response.setHeader("Content-Disposition", disposition);
-//
-//        // 4. 视频特殊处理(修正原逻辑错误)
-//        if (StrUtil.containsIgnoreCase(contentType, "video")) {
-//            response.setHeader("Accept-Ranges", "bytes"); // 支持分片
-//            // 原代码的 Content-Range 格式错误,已在 getFileContent 中正确设置,此处无需重复
-//        }
-//
-//        // 5. 输出内容
-//        IoUtil.write(response.getOutputStream(), false, content);
-
 
 
+        // ======== 浏览器缓存头(核心) ========
+        String contentType = getMineType(content, filename);
+        long maxAge;
+        // 图片:7天缓存(604800秒)
+        if (StrUtil.containsIgnoreCase(contentType, "image")) {
+            maxAge = 7 * 24 * 60 * 60;
+        }
+        // 视频(MP4/M3U8/TS):30天缓存(2592000秒)
+        else if (StrUtil.containsIgnoreCase(contentType, "video")
+                || filename.endsWith(".m3u8")
+                || filename.endsWith(".ts")) {
+            maxAge = 30 * 24 * 60 * 60;
+        }else {// 其他文件:1天缓存(可选)
+            maxAge = 24 * 60 * 60;
+        }
+        // 核心缓存头:让浏览器缓存资源
+        response.setHeader("Cache-Control", "public, max-age=" + maxAge);
+        // 兼容老浏览器(Expires为绝对时间)
+        response.setHeader("Expires", new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss z", Locale.US)
+                .format(new Date(System.currentTimeMillis() + maxAge * 1000)));
+        response.setHeader("Pragma", "public"); // 兼容HTTP/1.0
 
+        // ======== 原有逻辑(仅修复明显错误,不改动核心) ========
+        // 修复:Content-Disposition改为inline,让图片/视频在线播放(而非强制下载)
+        String disposition = (StrUtil.containsIgnoreCase(contentType, "image")
+                || StrUtil.containsIgnoreCase(contentType, "video")
+                || filename.endsWith(".m3u8")
+                || filename.endsWith(".ts"))
+                ? "inline; filename=" + HttpUtils.encodeUtf8(filename) // 在线播放
+                : "attachment; filename=" + HttpUtils.encodeUtf8(filename); // 其他文件下载
+        response.setHeader("Content-Disposition", disposition);
 
-        // 设置 header 和 contentType
-        //attachment(下载)、inline(预览)
-        response.setHeader("Content-Disposition", "attachment;filename=" + HttpUtils.encodeUtf8(filename));
-        String contentType = getMineType(content, filename);
+        // 修复:Content-Length设置为完整长度(原代码-1错误)
+        response.setContentLength(content.length);
         response.setContentType(contentType);
-        // 针对 video 的特殊处理,解决视频地址在移动端播放的兼容性问题
-        if (StrUtil.containsIgnoreCase(contentType, "video")) {
-            response.setHeader("Content-Length", String.valueOf(content.length - 1));
-            response.setHeader("Content-Range", String.valueOf(content.length - 1));
+
+        // 视频特殊处理:仅保留Accept-Ranges(去掉错误的Content-Range)
+        if (StrUtil.containsIgnoreCase(contentType, "video")
+                || filename.endsWith(".m3u8")
+                || filename.endsWith(".ts")) {
             response.setHeader("Accept-Ranges", "bytes");
         }
+
         // 输出附件
         IoUtil.write(response.getOutputStream(), false, content);
+
+
+//        // 设置 header 和 contentType
+//        //attachment(下载)、inline(预览)
+//        response.setHeader("Content-Disposition", "attachment;filename=" + HttpUtils.encodeUtf8(filename));
+//        String contentType = getMineType(content, filename);
+//        response.setContentType(contentType);
+//        // 针对 video 的特殊处理,解决视频地址在移动端播放的兼容性问题
+//        if (StrUtil.containsIgnoreCase(contentType, "video")) {
+//            response.setHeader("Content-Length", String.valueOf(content.length - 1));
+//            response.setHeader("Content-Range", String.valueOf(content.length - 1));
+//            response.setHeader("Accept-Ranges", "bytes");
+//        }
+//        // 输出附件
+//        IoUtil.write(response.getOutputStream(), false, content);
     }
 
 }