file video ai idea
This commit is contained in:
@@ -95,21 +95,149 @@ public class FileServiceImpl implements FileService {
|
|||||||
if (Objects.nonNull(request) && StringUtils.isNotBlank(request.getParameter("filename"))) {
|
if (Objects.nonNull(request) && StringUtils.isNotBlank(request.getParameter("filename"))) {
|
||||||
filename = URLDecoder.decode(request.getParameter("filename"),"utf-8");
|
filename = URLDecoder.decode(request.getParameter("filename"),"utf-8");
|
||||||
}
|
}
|
||||||
response.setContentType(session.getServletContext().getMimeType(filename));
|
|
||||||
|
String mimeType = session.getServletContext().getMimeType(filename);
|
||||||
|
response.setContentType(mimeType);
|
||||||
String encodFilename = URLEncoder.encode(filename, "utf-8");
|
String encodFilename = URLEncoder.encode(filename, "utf-8");
|
||||||
response.setHeader("code", "200");
|
response.setHeader("code", "200");
|
||||||
response.setHeader("Cache-Control", "max-age=31556926");
|
response.setHeader("Cache-Control", "max-age=31556926");
|
||||||
|
|
||||||
|
// 检查是否为视频文件,如果是则支持Range请求
|
||||||
|
boolean isVideo = mimeType != null && mimeType.startsWith("video/");
|
||||||
|
|
||||||
if (!Boolean.TRUE.equals(display)) {
|
if (!Boolean.TRUE.equals(display)) {
|
||||||
response.setHeader("Content-Disposition",
|
response.setHeader("Content-Disposition",
|
||||||
String.format("attachment; filename=\"%1$s\"; filename*=utf-8''%1$s", encodFilename));
|
String.format("attachment; filename=\"%1$s\"; filename*=utf-8''%1$s", encodFilename));
|
||||||
}
|
}
|
||||||
try (OutputStream outputStream = response.getOutputStream()) {
|
|
||||||
fileStorage.download(fileRecord.getFilePath(), outputStream, fileRecord.getFileSize());
|
Long fileSize = fileRecord.getFileSize();
|
||||||
|
|
||||||
|
// 对于视频文件,支持Range请求
|
||||||
|
if (isVideo) {
|
||||||
|
handleVideoDownloadWithRange(fileRecord, request, response, fileSize);
|
||||||
|
} else {
|
||||||
|
// 普通文件下载
|
||||||
|
try (OutputStream outputStream = response.getOutputStream()) {
|
||||||
|
fileStorage.download(fileRecord.getFilePath(), outputStream, fileSize);
|
||||||
|
} catch (ClientAbortException e) {
|
||||||
|
log.warn("客户端主动断开连接: {}", e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 处理支持Range请求的视频文件下载
|
||||||
|
*/
|
||||||
|
private void handleVideoDownloadWithRange(FileRecord fileRecord, HttpServletRequest request,
|
||||||
|
HttpServletResponse response, Long fileSize) throws IOException {
|
||||||
|
|
||||||
|
// 设置支持Range请求的头部
|
||||||
|
response.setHeader("Accept-Ranges", "bytes");
|
||||||
|
|
||||||
|
if (fileSize != null) {
|
||||||
|
response.setHeader("Content-Length", String.valueOf(fileSize));
|
||||||
|
}
|
||||||
|
|
||||||
|
String rangeHeader = request.getHeader("Range");
|
||||||
|
|
||||||
|
if (rangeHeader != null && rangeHeader.startsWith("bytes=")) {
|
||||||
|
// 处理Range请求
|
||||||
|
handleRangeRequest(fileRecord, response, rangeHeader, fileSize);
|
||||||
|
} else {
|
||||||
|
// 普通请求,但设置正确的头部
|
||||||
|
if (fileSize != null) {
|
||||||
|
response.setContentLengthLong(fileSize);
|
||||||
|
}
|
||||||
|
try (OutputStream outputStream = response.getOutputStream()) {
|
||||||
|
fileStorage.download(fileRecord.getFilePath(), outputStream, fileSize);
|
||||||
|
} catch (ClientAbortException e) {
|
||||||
|
log.warn("客户端主动断开连接: {}", e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 处理HTTP Range请求
|
||||||
|
*/
|
||||||
|
private void handleRangeRequest(FileRecord fileRecord, HttpServletResponse response,
|
||||||
|
String rangeHeader, Long fileSize) throws IOException {
|
||||||
|
|
||||||
|
if (fileSize == null) {
|
||||||
|
// 如果没有文件大小信息,降级为普通下载
|
||||||
|
try (OutputStream outputStream = response.getOutputStream()) {
|
||||||
|
fileStorage.download(fileRecord.getFilePath(), outputStream, fileSize);
|
||||||
|
} catch (ClientAbortException e) {
|
||||||
|
log.warn("客户端主动断开连接: {}", e.getMessage());
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 解析Range头部,格式:bytes=start-end
|
||||||
|
String range = rangeHeader.substring(6); // 去掉 "bytes="
|
||||||
|
String[] ranges = range.split("-");
|
||||||
|
|
||||||
|
long start = 0;
|
||||||
|
long end = fileSize - 1;
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (ranges.length >= 1 && !ranges[0].isEmpty()) {
|
||||||
|
start = Long.parseLong(ranges[0]);
|
||||||
|
}
|
||||||
|
if (ranges.length >= 2 && !ranges[1].isEmpty()) {
|
||||||
|
end = Long.parseLong(ranges[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 确保范围有效
|
||||||
|
if (start < 0) start = 0;
|
||||||
|
if (end >= fileSize) end = fileSize - 1;
|
||||||
|
if (start > end) {
|
||||||
|
response.setStatus(HttpServletResponse.SC_REQUESTED_RANGE_NOT_SATISFIABLE);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
long contentLength = end - start + 1;
|
||||||
|
|
||||||
|
// 设置206 Partial Content响应
|
||||||
|
response.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT);
|
||||||
|
response.setHeader("Content-Range", String.format("bytes %d-%d/%d", start, end, fileSize));
|
||||||
|
response.setHeader("Content-Length", String.valueOf(contentLength));
|
||||||
|
|
||||||
|
// 下载指定范围的内容
|
||||||
|
downloadRange(fileRecord.getFilePath(), response.getOutputStream(), start, contentLength);
|
||||||
|
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
log.warn("无效的Range请求: {}", rangeHeader);
|
||||||
|
response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
|
||||||
} catch (ClientAbortException e) {
|
} catch (ClientAbortException e) {
|
||||||
log.warn("客户端主动断开连接: {}", e.getMessage());
|
log.warn("客户端主动断开连接: {}", e.getMessage());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 下载文件的指定范围
|
||||||
|
*/
|
||||||
|
private void downloadRange(String filePath, OutputStream outputStream, long start, long length) throws IOException {
|
||||||
|
try (InputStream inputStream = fileStorage.download(filePath)) {
|
||||||
|
// 跳过开始位置之前的字节
|
||||||
|
long skipped = inputStream.skip(start);
|
||||||
|
if (skipped != start) {
|
||||||
|
log.warn("跳过字节数不匹配,期望: {}, 实际: {}", start, skipped);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 读取指定长度的数据
|
||||||
|
byte[] buffer = new byte[8192];
|
||||||
|
long remaining = length;
|
||||||
|
int bytesRead;
|
||||||
|
|
||||||
|
while (remaining > 0 && (bytesRead = inputStream.read(buffer, 0, (int) Math.min(buffer.length, remaining))) != -1) {
|
||||||
|
outputStream.write(buffer, 0, bytesRead);
|
||||||
|
remaining -= bytesRead;
|
||||||
|
}
|
||||||
|
|
||||||
|
outputStream.flush();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
Reference in New Issue
Block a user