使用com.sun.net.httpserver搭建服务无法正常托管MP4视频文件如何解决
问题原因与修复方案
核心错误点
- 错误重复调用
sendResponseHeaders:HTTP响应头只能在发送响应体前发送一次,你在循环读取文件块时反复调用该方法,且第二个参数传入了当前读取块的长度而非整个文件的总长度,导致浏览器收到的响应元信息完全错误 - 缺少视频对应的
Content-Type响应头:浏览器无法识别资源为视频格式,不会调用视频渲染逻辑处理返回的二进制流 - 不支持HTTP范围请求:现代浏览器播放视频默认会使用范围请求分段加载资源,你的服务直接返回完整200响应,无法适配浏览器的分段加载逻辑,导致视频无法正常渲染
修复后代码示例
private static HttpHandler load(final String fileName) { return new HttpHandler() { @Override public void handle(HttpExchange exchange) throws IOException { File file = new File(fileName); if (!file.exists()) { exchange.sendResponseHeaders(404, -1); exchange.close(); return; } long fileLength = file.length(); String range = exchange.getRequestHeaders().getFirst("Range"); long start = 0; long end = fileLength - 1; int statusCode = 200; // 处理范围请求 if (range != null && range.startsWith("bytes=")) { String[] rangeParts = range.substring(6).split("-"); try { start = Long.parseLong(rangeParts[0]); if (rangeParts.length > 1 && !rangeParts[1].isEmpty()) { end = Long.parseLong(rangeParts[1]); } } catch (NumberFormatException ignored) {} if (start > end || start >= fileLength) { exchange.getResponseHeaders().set("Content-Range", "bytes */" + fileLength); exchange.sendResponseHeaders(416, -1); exchange.close(); return; } statusCode = 206; exchange.getResponseHeaders().set("Content-Range", String.format("bytes %d-%d/%d", start, end, fileLength)); } long contentLength = end - start + 1; // 设置正确的Content-Type,可根据文件后缀调整,这里以mp4为例 exchange.getResponseHeaders().set("Content-Type", "video/mp4"); exchange.getResponseHeaders().set("Accept-Ranges", "bytes"); exchange.getResponseHeaders().set("Content-Length", String.valueOf(contentLength)); // 响应头只发送一次 exchange.sendResponseHeaders(statusCode, contentLength); try (FileInputStream fin = new FileInputStream(file); OutputStream os = exchange.getResponseBody()) { fin.skip(start); byte[] buffer = new byte[0x10000]; int count; long remaining = contentLength; while (remaining > 0 && (count = fin.read(buffer, 0, (int)Math.min(buffer.length, remaining))) > 0) { os.write(buffer, 0, count); remaining -= count; } } finally { exchange.close(); } } }; }
额外注意事项
- 如果需要支持多种视频格式,可通过文件名后缀动态设置
Content-Type,比如webm对应video/webm,ogg对应video/ogg - 大文件建议使用NIO的
FileChannel.transferTo替代普通IO流读写,性能更高,减少内存占用
内容的提问来源于stack exchange,提问作者Krusty the Clown
相关产品推荐
相关产品推荐

