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

时间:2015-12-25 02:18:33

标签: python google-chrome http firefox http-headers

我有一个使用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标头才能使其正常工作?

修改

这是我现在的代码:

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。< / p>

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

其他信息

发送到客户端的HTTP响应标头:

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

3 个答案:

答案 0 :(得分:1)

要播放视频,您应该至少支持range requeststransfer-encoding: chunked

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

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

您也可以在没有任何Python代码的情况下使用,例如如果您使用nginx

答案 1 :(得分:1)

从根本上说,@ qarma是对的。要流式传输视频,您需要使用至少支持Range:标题的库或框架。

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

如果您想阻止此流式传输行为并强制浏览器下载该文件而不播放该文件,请返回Content-Disposition标题,强制它将视频文件视为下载文件而不是内容要渲染。

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

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似乎忽略了这一点。

答案 2 :(得分:1)

事实证明我正在使用的视频文件已损坏(可能它有一些丢帧或类似的东西)。我和其他多个.mp4一起测试过,它就像一个魅力。

就响应的http标头而言,您真正需要播放视频文件(或者更准确地按@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标头也不是强制性的(它们由SimpleHTTPRequestHandler自动发送)。

正如@qarma和@hrunting所指出的,如果你想让用户跳转到视频中的特定时间,你应该支持Range标题。支持Chrome默认发送的Range标头只是一个好主意。