如何使用 Python 和 Flask 转发 HTTP 范围请求?

koa*_*alo 5 python http flask

我有一个 Flask 应用程序,它将提供一个端点来下载一个大文件。但是,不是从文件系统提供它或即时生成文件,而是必须首先通过 HTTP 从另一台服务器下载该文件。

当然,我可以先向外部服务器执行 GET 请求,完整下载文件并将其存储在文件系统或内存中,然后作为第二步提供原始请求的结果。这看起来像这样(还包括一个基本的身份验证,以表明为什么在较低层上的简单代理是不够的):

#!flask/bin/python
from flask import Flask, jsonify
import os
import requests
from requests.auth import HTTPBasicAuth

app = Flask(__name__)

@app.route('/download')
def download():
    auth = HTTPBasicAuth("some_user", "some_password")
    session = requests.Session()
    session.auth = auth
    response = session.get("http://example.com")
    return response.content

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=1234, debug=True)
Run Code Online (Sandbox Code Playgroud)

但是,这会增加应用程序的延迟和存储要求。而且,即使接收方只需要执行文件的部分下载(即执行 HTTP 范围请求),也必须首先从外部服务器完全获取文件。

有没有更优雅的选择来解决这个问题,即为直接转发到外部服务器的 HTTP 范围请求提供支持?

Aro*_*unt 7

根据Proxying to another web service with FlaskDownload large file in python with requestsFlask large file download我设法在流模式下制作了一个 Flask HTTP 代理。

from flask import Flask, request, Response
import requests

PROXY_URL = 'http://ipv4.download.thinkbroadband.com/'

def download_file(streamable):
    with streamable as stream:
        stream.raise_for_status()
        for chunk in stream.iter_content(chunk_size=8192):
            yield chunk


def _proxy(*args, **kwargs):
    resp = requests.request(
        method=request.method,
        url=request.url.replace(request.host_url, PROXY_URL),
        headers={key: value for (key, value) in request.headers if key != 'Host'},
        data=request.get_data(),
        cookies=request.cookies,
        allow_redirects=False,
        stream=True)

    excluded_headers = ['content-encoding', 'content-length', 'transfer-encoding', 'connection']
    headers = [(name, value) for (name, value) in resp.raw.headers.items()
               if name.lower() not in excluded_headers]

    return Response(download_file(resp), resp.status_code, headers)


app = Flask(__name__)

@app.route('/', defaults={'path': ''})
@app.route('/<path:path>')
def download(path):
    return _proxy()

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=1234, debug=True)
Run Code Online (Sandbox Code Playgroud)

download_file() 将以流模式打开请求,并在流传输后立即生成每个块。

_proxy()创建请求,然后Response使用迭代器download_file()作为内容创建并返回一个 Flask 。

我使用https://www.thinkbroadband.com/download对其进行了测试,其中有几个存档文件可以免费下载以进行测试。(小心,档案已损坏,因此您最好使用校验和来确保您获得了预期的文件)。

一些例子:

curl 'http://0.0.0.0:1234/100MB.zip' --output /tmp/100MB.zip
curl 'http://0.0.0.0:1234/20MB.zip' --output /tmp/20MB.zip
Run Code Online (Sandbox Code Playgroud)

我还在随机网站上进行了一些其他测试以获得大图像。到目前为止,我没有遇到任何问题。