You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.26 10:15:05