浏览器在响应完全下载之前关闭套接字

Browsers close socket before the response is fully downloaded

我有一个使用 http.server 的简单 Python 服务器。目的不是在 html 页面中显示视频,也不是下载视频文件,而是直接在浏览器中显示视频。这是我目前所拥有的:

import http.server

class SuperHandler(http.server.SimpleHTTPRequestHandler):
    def do_GET(self):
        path = self.path
        encodedFilePath = 'file.mp4'

        with open(encodedFilePath, 'rb') as videoFile:
            self.send_response(200)
            self.send_header('Content-type', 'video/mp4')
            self.end_headers()
            self.wfile.write(videoFile.read())
            print('File sent: ' + videoFile.name)

server_address = ('', 8000)
handler_class = SuperHandler
httpd = http.server.HTTPServer(server_address, handler_class)
httpd.serve_forever()

我遇到的问题是响应不包含完整视频。 file.mp4 是 50MB,但是当我查看 Chrome 或 Firefox 的网络选项卡时,它说响应只有 1MB。是否有未传输完整文件的原因?我是否需要添加某种 HTTP header 才能使其正常工作?

编辑:

现在这是我的代码:

server_address = ('', 8000)
handler_class = http.server.SimpleHTTPRequestHandler
httpd = http.server.HTTPServer(server_address, handler_class)

httpd.serve_forever()

我现在正在使用默认的 SimpleHTTPRequestHandler do_GET,但它仍然无法正常工作(虽然现在响应是 40MB/30MB 而不是 1MB)。

当我在 Chrome 上请求 file.mp4 时,套接字连接在约 7 秒后关闭(在 Firefox 上为约 5 秒),这使得脚本抛出 BrokenPipeError: [Errno 32] Broken pipebecause the server is still trying to write the rest of the video file on a closed socket.

所以我的问题是:如何让浏览器在关闭套接字之前下载完整的响应?

附加信息

发送给客户端的 HTTP 响应 header:

HTTP/1.0 200 OK
Server: SimpleHTTP/0.6 Python/3.5.0
Date: Mon, 28 Dec 2015 02:36:39 GMT
Content-type: video/mp4
Content-Length: 53038876
Last-Modified: Fri, 25 Dec 2015 02:09:52 GMT

要流式传输您至少应支持的视频 range requests and transfer-encoding: chunked

据我所知http.server不直接支持。你当然可以在上面实现它。

或者使用一个简单的框架,例如bottle(一个文件,已经支持两者)或cherrypy(更可靠,多线程等)

您也可以不使用任何 Python 代码,例如如果您使用 nginx

从根本上说,@qarma 是对的。要流式传输视频,您需要使用至少支持 Range: headers.

的库或框架

但是,您没有/尝试/流式传输视频。浏览器正在为你做这件事。当您 return video/mp4 内容类型时,知道如何流式传输视频本身的浏览器会立即切换到流式传输模式。它停止了它正在进行的文件下载(管道错误的来源),并以 Range: header 重新启动。它利用浏览器中现有的媒体播放器代码来实现这一点。由于 SimpleHTTPServer class 不支持 Range: header,因此无法正确处理响应。

如果您想阻止这种流式传输行为并强制浏览器下载文件而不播放它,那么 return Content-Disposition header 强制浏览器将视频文件视为下载的文件而不是要呈现的内容。

下面是一些基于您最初提出的问题的代码,可以满足您的需求:

import http.server

class SuperHandler(http.server.SimpleHTTPRequestHandler):
    def do_GET(self):
        path = self.path
        encodedFilePath = 'file.mp4'

        with open(encodedFilePath, 'rb') as videoFile:
            self.send_response(200)
            self.send_header('Content-type', 'video/mp4')
            self.send_header('Content-Disposition', 'attachment; filename=' + encodedFilePath)
            self.end_headers()
            self.copyfile(videoFile, self.wfile)

server_address = ('', 8000)
handler_class = SuperHandler
httpd = http.server.HTTPServer(server_address, handler_class)
httpd.serve_forever()

理论上,您也发回 Accept-Ranges: none,但 Chrome,至少,似乎忽略了这一点。

事实证明我使用的视频文件已损坏(可能有一些丢帧或类似问题)。我用其他多个 .mp4 进行了测试,效果非常好。

就响应的 http headers 而言,播放视频文件(或更准确地流式传输视频文件,如@hrunting 所指)真正需要的是:

HTTP/1.0 200 OK
Server: SimpleHTTP/0.6 Python/3.5.0
Date: Sat, 02 Jan 2016 02:45:34 GMT
Content-type: video/mp4
Content-Length: 33455269
Last-Modified: Sat, 02 Jan 2016 02:45:27 GMT

我认为甚至 ServerDateLast-Modified header 也不是强制性的(它们由 SimpleHTTPRequestHandler 自动发送)。

正如@qarma 和@hrunting 所指出的,如果你想让用户跳转到视频中的特定时间,你应该支持 Range header。支持 Range header 是个好主意,因为它默认由 Chrome.

发送