浏览器在响应完全下载之前关闭套接字
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 pipe
,because 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
我认为甚至 Server
、Date
和 Last-Modified
header 也不是强制性的(它们由 SimpleHTTPRequestHandler
自动发送)。
正如@qarma 和@hrunting 所指出的,如果你想让用户跳转到视频中的特定时间,你应该支持 Range
header。支持 Range
header 是个好主意,因为它默认由 Chrome.
发送
我有一个使用 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 pipe
,because 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
我认为甚至 Server
、Date
和 Last-Modified
header 也不是强制性的(它们由 SimpleHTTPRequestHandler
自动发送)。
正如@qarma 和@hrunting 所指出的,如果你想让用户跳转到视频中的特定时间,你应该支持 Range
header。支持 Range
header 是个好主意,因为它默认由 Chrome.