扩展|web|content的视频传输,ios单独处理
True

odoo web/content 返回的文件传输 是无脑返回一整个文件,没对视频单独处理
对于chrome没有问题,只能说chrome兼容性做得比较好,并不是说视频传输协议就可以这样。

而对于safari来说,他不是一次性请求全部文件的(不论osx还是ios),一般首先会请求0-1字节,这个写在request header的"range"字段中:
range:'bytes=0-1'
如果是想要传输视频,必须要解析range字段,然后按照range字段的要求返回对应的数据,同时response header至少要包含三个字段:"Content-Type", "Content-Range", "Content-Length"
"Content-Type"必需明确指定视频格式,有"video/mp4", "video/ogg", "video/mov"等等,网上可以查到。
"Content-Range"格式是 "bytes <start>-<end>/<total>",其中start和end必需对应request header里的range字段,total是文件总大小,不是返回的数据长度
"Content-Length"指定返回的二进制长度

然后有几个坑:
1.chrome有时候会一次性请求全部内容,range是这样的 'bytes=0-',解析的时候需要注意
2.所有的end是指inclusive end,意味着文件长度如果是245,返回"Content-Range"就是"bytes 0-244/245",错一点视频就放不出来了(包括chrome)

修改底层代码:

@http.route(['/web/content',
'/web/content/<string:xmlid>',
'/web/content/<string:xmlid>/<string:filename>',
'/web/content/<int:id>',
'/web/content/<int:id>/<string:filename>',
'/web/content/<int:id>-<string:unique>',
'/web/content/<int:id>-<string:unique>/<string:filename>',
'/web/content/<int:id>-<string:unique>/<path:extra>/<string:filename>',
'/web/content/<string:model>/<int:id>/<string:field>',
'/web/content/<string:model>/<int:id>/<string:field>/<string:filename>'], type='http', auth="public",
csrf=False)
def content_common(self, xmlid=None, model='ir.attachment', id=None, field='datas',
filename=None, filename_field='datas_fname', unique=None, mimetype=None,
download=None, data=None, token=None, access_token=None, related_id=None, access_mode=None,
**kw):
status, headers, content = binary_content(
xmlid=xmlid, model=model, id=id, field=field, unique=unique, filename=filename,
filename_field=filename_field, download=download, mimetype=mimetype,
access_token=None, related_id=related_id, access_mode=access_mode)
if status == 304:
response = werkzeug.wrappers.Response(status=status, headers=headers)
elif status == 301:
return werkzeug.utils.redirect(content, code=301)
elif status != 200:
response = request.not_found()
else:
content_base64 = base64.b64decode(content)
http_headers = request.httprequest.headers
# headers.append(('Content-Length', len(content_base64)))
if http_headers.environ.get('HTTP_RANGE'):
headers.append(("Accept-Ranges", 'bytes'))
range_value = http_headers.environ['HTTP_RANGE']
# # 取出start与end
HTTP_RANGE_HEADER = re.compile(r'bytes=([0-9]+)\-(([0-9]+)?)')
m = re.match(HTTP_RANGE_HEADER, range_value)
if m:
start_str = m.group(1)
start = int(start_str)
end_str = m.group(2)
end = -1
# end存在
if len(end_str) > 0:
end = int(end_str)
# range存在时,让请求支持断点续传,status_code改为206
# 图省事这里把content-type写为*
headers.append(("Content-Type", '*'))
if end == -1:
# 此处的md['size']是文件大小
headers.append(("Content-Length", str(len(content_base64) - start)))
else:
# Content-Length也要改变
headers.append(("Content-Length", str(end - start + 1)))
headers.append(("Accept-Ranges", 'bytes'))
if end < 0:
content_range_header_value = "bytes %d-%d/%d" % (
start, len(content_base64) - 1, len(content_base64))
end = len(content_base64) - 1
else:
content_range_header_value = "bytes %d-%d/%d" % (start, end, len(content_base64))
end += 1
headers.append(("Content-Range", content_range_header_value))
content_base64 = content_base64[start: end+1]
response = request.make_response(content_base64, headers)
# response.status_code = 206
if token:
response.set_cookie('fileToken', token)
return response


tree button更新context信息
True