| | |
| | | import org.springframework.web.bind.annotation.*; |
| | | import org.springframework.web.multipart.MultipartFile; |
| | | |
| | | import javax.servlet.http.HttpServletRequest; |
| | | import javax.servlet.http.HttpServletResponse; |
| | | import java.io.*; |
| | | import java.nio.file.Files; |
| | |
| | | * 播放视频文件 |
| | | */ |
| | | @GetMapping("/play/{fileName}") |
| | | public void playVideo(@PathVariable("fileName") String fileName, HttpServletResponse response) { |
| | | public void playVideo(@PathVariable("fileName") String fileName, HttpServletRequest request, HttpServletResponse response) { |
| | | try { |
| | | String companyId = getCurrentUserCompanyId(); |
| | | String uploadDir = baseUploadDir + "/" + companyId; |
| | |
| | | return; |
| | | } |
| | | |
| | | // 设置响应头 |
| | | response.setContentType("video/mp4"); |
| | | response.setContentLengthLong(Files.size(filePath)); |
| | | // 获取文件大小 |
| | | long fileSize = Files.size(filePath); |
| | | |
| | | // 检查是否有Range请求头 |
| | | String rangeHeader = request.getHeader("Range"); |
| | | if (rangeHeader != null) { |
| | | // 解析Range头,格式为"bytes=start-end" |
| | | String[] ranges = rangeHeader.replace("bytes=", "").split("-"); |
| | | long start = 0, end = fileSize - 1; |
| | | |
| | | if (!ranges[0].isEmpty()) { |
| | | start = Long.parseLong(ranges[0]); |
| | | } |
| | | if (ranges.length > 1 && !ranges[1].isEmpty()) { |
| | | end = Long.parseLong(ranges[1]); |
| | | } else { |
| | | end = fileSize - 1; |
| | | } |
| | | |
| | | // 确保范围有效 |
| | | if (start > end || start >= fileSize) { |
| | | response.setStatus(HttpStatus.REQUESTED_RANGE_NOT_SATISFIABLE.value()); |
| | | return; |
| | | } |
| | | |
| | | // 限制结束位置不超过文件大小 |
| | | if (end >= fileSize) { |
| | | end = fileSize - 1; |
| | | } |
| | | |
| | | // 计算内容长度 |
| | | long contentLength = end - start + 1; |
| | | |
| | | // 设置响应状态和头部信息 |
| | | response.setStatus(HttpStatus.PARTIAL_CONTENT.value()); |
| | | response.setContentType(getContentTypeByExtension(fileName)); |
| | | response.setContentLengthLong(contentLength); |
| | | response.setHeader("Content-Range", "bytes " + start + "-" + end + "/" + fileSize); |
| | | response.setHeader("Accept-Ranges", "bytes"); |
| | | response.setHeader("Content-Disposition", "inline; filename=\"" + fileName + "\""); |
| | | |
| | | // 从指定位置开始读取文件 |
| | | try (RandomAccessFile randomAccessFile = new RandomAccessFile(filePath.toFile(), "r"); |
| | | OutputStream outputStream = response.getOutputStream()) { |
| | | |
| | | randomAccessFile.seek(start); |
| | | byte[] buffer = new byte[8192]; |
| | | long remaining = contentLength; |
| | | int bytesRead; |
| | | |
| | | while (remaining > 0 && (bytesRead = randomAccessFile.read(buffer, 0, (int) Math.min(buffer.length, remaining))) != -1) { |
| | | outputStream.write(buffer, 0, bytesRead); |
| | | remaining -= bytesRead; |
| | | } |
| | | } |
| | | } else { |
| | | // 没有Range头,返回完整文件 |
| | | response.setStatus(HttpStatus.OK.value()); |
| | | response.setContentType(getContentTypeByExtension(fileName)); |
| | | response.setContentLengthLong(fileSize); |
| | | response.setHeader("Accept-Ranges", "bytes"); |
| | | response.setHeader("Content-Disposition", "inline; filename=\"" + fileName + "\""); |
| | | |
| | | // 流式传输文件 |
| | |
| | | outputStream.write(buffer, 0, bytesRead); |
| | | } |
| | | } |
| | | } |
| | | } catch (Exception e) { |
| | | e.printStackTrace(); |
| | | response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value()); |
| | |
| | | } |
| | | |
| | | /** |
| | | * 根据文件扩展名获取MIME类型 |
| | | */ |
| | | private String getContentTypeByExtension(String fileName) { |
| | | String extension = ""; |
| | | int lastDotIndex = fileName.lastIndexOf('.'); |
| | | if (lastDotIndex > 0) { |
| | | extension = fileName.substring(lastDotIndex).toLowerCase(); |
| | | } |
| | | |
| | | switch (extension) { |
| | | case ".mp4": |
| | | return "video/mp4"; |
| | | case ".avi": |
| | | return "video/x-msvideo"; |
| | | case ".mov": |
| | | return "video/quicktime"; |
| | | case ".wmv": |
| | | return "video/x-ms-wmv"; |
| | | case ".flv": |
| | | return "video/x-flv"; |
| | | case ".webm": |
| | | return "video/webm"; |
| | | case ".mkv": |
| | | return "video/x-matroska"; |
| | | case ".m3u8": |
| | | return "application/vnd.apple.mpegurl"; |
| | | case ".ts": |
| | | return "video/MP2T"; |
| | | default: |
| | | return "application/octet-stream"; |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 获取文件列表 |
| | | */ |
| | | @GetMapping("/list") |