File Upload with Tornado

Tornado Application download
#!/usr/bin/env python
# coding:utf-8

import os import uuid

from fs import open_fs from tornado.web import Application, RequestHandler, url, StaticFileHandler

from tornado.httpserver import HTTPServer from tornado.ioloop import IOLoop

fs = open_fs('osfs://uploads/')

class Userform(RequestHandler):

<span class="k">def</span> <span class="nf">get</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
    <span class="bp">self</span><span class="o">.</span><span class="n">render</span><span class="p">(</span><span class="s2">&#34;upload.html&#34;</span><span class="p">)</span>

# noinspection PyAbstractClass class AsyncFileUploadHandler(RequestHandler): """ Uploader """

<span class="k">def</span> <span class="nf">upload_async</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">filepart</span><span class="p">):</span>
    <span class="n">filename</span> <span class="o">=</span> <span class="n">filepart</span><span class="p">[</span><span class="s1">&#39;filename&#39;</span><span class="p">]</span>
    <span class="n">extension</span> <span class="o">=</span> <span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">splitext</span><span class="p">(</span><span class="n">filename</span><span class="p">)[</span><span class="mi">1</span><span class="p">]</span>
    <span class="n">unique_name</span> <span class="o">=</span> <span class="nb">str</span><span class="p">(</span><span class="n">uuid</span><span class="o">.</span><span class="n">uuid4</span><span class="p">())</span> <span class="o">+</span> <span class="n">extension</span>
    <span class="n">fs</span><span class="o">.</span><span class="n">setbytes</span><span class="p">(</span><span class="n">unique_name</span><span class="p">,</span> <span class="n">filepart</span><span class="p">[</span><span class="s1">&#39;body&#39;</span><span class="p">])</span>
    <span class="k">return</span> <span class="n">unique_name</span>

<span class="k">def</span> <span class="nf">post</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
    <span class="n">files</span> <span class="o">=</span> <span class="nb">tuple</span><span class="p">(</span><span class="nb">map</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">upload_async</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">request</span><span class="o">.</span><span class="n">files</span><span class="p">[</span><span class="s1">&#39;file&#39;</span><span class="p">]))</span>
    <span class="n">urls</span> <span class="o">=</span> <span class="nb">tuple</span><span class="p">(</span><span class="nb">map</span><span class="p">(</span><span class="k">lambda</span> <span class="n">name</span><span class="p">:</span> <span class="sa">f</span><span class="s1">&#39;/uploads/</span><span class="si">{</span><span class="n">name</span><span class="si">}</span><span class="s1">&#39;</span><span class="p">,</span> <span class="n">files</span><span class="p">))</span>
    <span class="n">data</span> <span class="o">=</span> <span class="nb">dict</span><span class="p">(</span><span class="n">files</span><span class="o">=</span><span class="n">files</span><span class="p">,</span> <span class="n">urls</span><span class="o">=</span><span class="n">urls</span><span class="p">)</span>
    <span class="bp">self</span><span class="o">.</span><span class="n">finish</span><span class="p">(</span><span class="n">data</span><span class="p">)</span>

handlers = [ (r"/", Userform), url(pattern=r'/upload', handler=AsyncFileUploadHandler, name='upload'), (r"/uploads/(.*)", StaticFileHandler, {"path": "uploads"}) ]

App = Application(handlers=handlers, **dict(debug=False, autoreload=False, logging='debug')) if name == "main": server = HTTPServer(App) server.bind(port=8000) server.start(num_processes=1) IOLoop.current().start()

Docker Compose download
version: "3"

services:
  app:
    build: .
    volumes:
      -  ./uploads:/uploads
    ports:
      - "8000:8000"
Dockerfile download
FROM guneysu/python:3-onbuild
VOLUME ["/uploads"]
ADD app /app
WORKDIR /
CMD python -m app
Requirements download
fs==2.0.4
tornado==4.5.1
Makefile download
default: run

build: @docker-compose build

run: build @docker-compose up

test: @http -f POST :8000/upload [email protected] [email protected]

down: @docker-compose down

develop: @docker-compose up –build

.PHONY: default build run test down develop

Running

docker-compose up -d

Testing

http -f POST :8000/ [email protected]
HTTP/1.1 200 OK
Content-Length: 215
Content-Type: application/json; charset=UTF-8
Date: Sat, 02 May 2020 14:36:36 GMT
Server: TornadoServer/4.5.1

{
    "files": [
        "6936cd27-01dc-4af6-adf9-970ad168ed6b.txt",
        "1c7052d7-0e29-4eb4-a914-f58d78289add.txt"
    ],
    "urls": [
        "/uploads/6936cd27-01dc-4af6-adf9-970ad168ed6b.txt",
        "/uploads/1c7052d7-0e29-4eb4-a914-f58d78289add.txt"
    ]
}