├── .gitignore ├── README.md ├── requirements.txt └── streaming.py /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | env/ 12 | build/ 13 | develop-eggs/ 14 | dist/ 15 | downloads/ 16 | eggs/ 17 | .eggs/ 18 | lib/ 19 | lib64/ 20 | parts/ 21 | sdist/ 22 | var/ 23 | wheels/ 24 | *.egg-info/ 25 | .installed.cfg 26 | *.egg 27 | 28 | # PyInstaller 29 | # Usually these files are written by a python script from a template 30 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 31 | *.manifest 32 | *.spec 33 | 34 | # Installer logs 35 | pip-log.txt 36 | pip-delete-this-directory.txt 37 | 38 | # Unit test / coverage reports 39 | htmlcov/ 40 | .tox/ 41 | .coverage 42 | .coverage.* 43 | .cache 44 | nosetests.xml 45 | coverage.xml 46 | *.cover 47 | .hypothesis/ 48 | 49 | # Translations 50 | *.mo 51 | *.pot 52 | 53 | # Django stuff: 54 | *.log 55 | local_settings.py 56 | 57 | # Flask stuff: 58 | instance/ 59 | .webassets-cache 60 | 61 | # Scrapy stuff: 62 | .scrapy 63 | 64 | # Sphinx documentation 65 | docs/_build/ 66 | 67 | # PyBuilder 68 | target/ 69 | 70 | # Jupyter Notebook 71 | .ipynb_checkpoints 72 | 73 | # pyenv 74 | .python-version 75 | 76 | # celery beat schedule file 77 | celerybeat-schedule 78 | 79 | # SageMath parsed files 80 | *.sage.py 81 | 82 | # dotenv 83 | .env 84 | 85 | # virtualenv 86 | .venv 87 | venv/ 88 | ENV/ 89 | 90 | # Spyder project settings 91 | .spyderproject 92 | .spyproject 93 | 94 | # Rope project settings 95 | .ropeproject 96 | 97 | # mkdocs documentation 98 | /site 99 | 100 | # mypy 101 | .mypy_cache/ 102 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # python_flask_file_streaming 2 | Example of streaming files for upload/download and both (proxy) in Python Flask. 3 | 4 | For more detailed explanation see my post: http://izmailoff.github.io/web/flask-file-streaming. 5 | 6 | # Quick Start 7 | Clone this repo: 8 | 9 | git clone git@github.com:izmailoff/python_flask_file_streaming.git 10 | 11 | Navigate to the project: 12 | 13 | cd python_flask_file_streaming 14 | 15 | Create a virtual environment. One option is: 16 | 17 | python3.6 -m venv env 18 | 19 | Activate the environment: 20 | 21 | source env/bin/activate 22 | 23 | Install all dependencies (flask, requests): 24 | 25 | pip install -r requirements.txt 26 | 27 | Edit this line: 28 | 29 | resp = requests.post('http://destination_host/upload_api', files={'file': request.stream}) 30 | 31 | in `streaming.py` to set correct upload host, and optionally adjust other parameters of the `post` request. See the comments in the code for more details. 32 | 33 | Start the application: 34 | 35 | python streaming.py 36 | 37 | Upload a file to one of the APIs. For example: 38 | 39 | curl -F "file=@any_file_binary_or_text.ext" http://127.0.0.1:5000/proxy 40 | 41 | Profit. 42 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | Flask 2 | requests 3 | -------------------------------------------------------------------------------- /streaming.py: -------------------------------------------------------------------------------- 1 | from flask import Flask 2 | from flask import Response 3 | from flask import stream_with_context 4 | import requests 5 | from flask import request 6 | 7 | app = Flask(__name__) 8 | 9 | 10 | # Upload file as stream to a file. 11 | @app.route("/upload", methods=["POST"]) 12 | def upload(): 13 | with open("/tmp/output_file", "bw") as f: 14 | chunk_size = 4096 15 | while True: 16 | chunk = request.stream.read(chunk_size) 17 | if len(chunk) == 0: 18 | return 19 | f.write(chunk) 20 | 21 | 22 | # Download from provided URL. 23 | @app.route('/') 24 | def download(url): 25 | req = requests.get(url, stream=True) 26 | return Response(stream_with_context(req.iter_content()), content_type=req.headers['content-type']) 27 | 28 | 29 | # Proxy uploaded files as stream to another web API without saving them to disk or holding them in memory. 30 | # This example uses multipart form data upload for both this API and destination API. 31 | # 32 | # Test this endpoint with: 33 | # curl -F "file=@some_binary_file.pdf" http://127.0.0.1:5000/proxy 34 | @app.route("/proxy", methods=["POST"]) 35 | def proxy(): 36 | # use data=... or files=..., etc in the call below - this affects the way data is POSTed: form-encoded for `data`, 37 | # multipart encoding for `files`. See the code/docs for more details here: 38 | # https://github.com/requests/requests/blob/master/requests/api.py#L16 39 | resp = requests.post('http://destination_host/upload_api', files={'file': request.stream}) 40 | return resp.text, resp.status_code 41 | 42 | 43 | if __name__ == '__main__': 44 | app.run() 45 | --------------------------------------------------------------------------------