How to Upload Files with Python Tornado Framework: Step-by-Step Tutorial
Overview
In this quick tutorial we will be working towards developing a file upload functionality for a Tornado webserver with Python. We will do the following: (1) setup the folder structure, create a virtual environment, add the list of necessary libraries, and install our dependencies; (2) we will create the webserver and all the supporting files; and (3) we will test our application. We will build this as a REST API with a plan HTML file that will be rendered as a webpage from a template and use our REST API.
Setup
We are going to use VS Code for our IDE, but first we are going to create the following folder structure:
/<project name>
/templates
/static
/uploads
On the root of the folder we will create two files: (1) requirements.txt; (2) server.py; and (3) a file named uploads.html in the templates folder. After opening the project folder on your IDE we will see the following image.
Now with all our project structure setup we are going to create our virtual environment in python and then activating it by executing the following command on the terminal.
PS D:\dev\Medium\tornado-upload> python -m venv .venv
PS D:\dev\Medium\tornado-upload> .\.venv\Scripts\activate
Next we are to add our libraries to the requirements.txt.
tornado >= 6.4.1
We will execute pip to fetch our libraries, and then we will update pip for our virtual environment.
(.venv) PS D:\dev\Medium\tornado-upload> pip install -r .\requirements.txt
...
(.venv) PS D:\dev\Medium\tornado-upload> python -m pip install --upgrade pip
...
The libraries needed should be installed at this point, and we can focus on developing the web server as a next step.
Web Server
The webserver will be based on our previous tutorials, I suggest you read the following tutorial on using Tornado in order to create the barebones of the application. The BaseHandler, and ErrorHandler should be the same. We focus on the FileUploadHandler, StatusHandler, UploadHandler, and making the Tornado application.
The FileUploadHandler is the main class for this project, it will handle receiving a POST call that will contain the file. We will obtain the file through the request object using self.request.files[‘file’][0] and assign it to a variable named fileinfo. Then we will extract the follwing: (1) file name using the fileinfo[‘filename’] and assign it to a variable named filename; (2) then the content type from the fileinfo[‘content_type’] and assign it to a variable named content_type; and (3) the data of the file from fileinfo[‘body’] and assign it to the file_body variable. With the variables all assigned, we will create a file in the uploads folder, and write the contents to it in lines 13-15. Finally in lines 17-19, we will set the status to 200, write a JSON message with the name of the file and the content type, and finish the REST call with the finish method. The snippet below show the FileUploadHandler.
Next, we are going to create the StatusHandler and the UploadHandler. The StatusHandler will set the status of the response to 200, write a message as a JSON object, and call the finish method. The UploadHandler will render the uploads.html file within our templates folder, by using the render method. The code below shows the classes, notice we are using the async function modifier to leverage concurrency features built into the Tornado webserver.
Finally we are going to have the top level environment ‘__main__’ call a function named main, which in turn will call a function named make_app.
The fully functioning webserver is completed, we will run it next.
Running
We will use the following command to run our Tornado webserver from our Terminal view in VS Code.
python .\server.py
Once we run the command the terminal will show that it has started and the url along with the port, in this case it will be the localhost on port 8888.
Testing
We are going to test our REST API by doing the following: (1) accessing the root of the web application to make sure the server is running; and (2) uploading the Python logo image through the simple webform on the /uploads route. We will use the image below for testing, it is also contained in the GitHub repository under the test folder.
Testing The Server Status
We are going to access the https://localhost:8888 to verify that our server is running accordingly, the result will be shown in your browser similar to the image below.
Testing File Upload Functionality
We will be testing the file upload functionality by accessing the /upload route on the webserver, navigating to http://localhost:8888/upload will show you the following web application.
Using the “Choose File” button, we will navigate through our machine and find a file to upload, in this case it will be the Python logo image.
We will now click on the “Upload File” button. Once the file is uploaded we will get the name of the file, and also the type of file as a successful upload message show in the figure below.
Now we will verify the uploaded file in the uploads folder and we should have the Python-logo-notext-rotate.png image in the folder as shown below.
At this point we have a working solution, now as a homework for the reader I would challenge them to test uploading a file with the same name twice.
Conclusion
We have completed a REST API that allows us to upload files using the Tornado web framework. The full source code of the application can be found on my GitHub repository.
https://github.com/an01f01/tornado-upload
References
Subscribe to my newsletter
Read articles from Alessandro directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
Alessandro
Alessandro
I am a developer from the Greater Boston area, I have a M.S. in Computer Science and Engineering from the University of Connecticut, focused on Bayesian Networks and Bayesian Knowledge Bases. I have experience working on 3D laser scanners, motion platforms, DARPA projects, 3D simulations using the Unity game engine for robotics in synthetic environments (game generated and scanned), mobile development in both Android and iOS. My strengths are in rapid prototyping and taking an idea into a release candidate. The process is a winding road that takes a lot of time