|
|
@@ -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);
|
|
|
}
|
|
|
|
|
|
}
|