Create a Simple File Upload Service Using Python Flask

Introduction

In daily work, file upload is a common requirement. For example, collecting dump files generated by scripts when an application encounters an error for analysis, but implementing this feature can be quite complex. Fortunately, the Flask framework provides a simple and efficient way to handle file uploads, with less than 100 lines of code. In this article, we will explore how to implement file upload functionality using Flask, and write a Dockerfile to deploy the application via Docker.

Flask File Upload Process

The basic process of file upload in Flask includes the following steps:

  1. Create a form with the enctype=multipart/form-data attribute, and place a element inside it.
  2. The application accesses the file through the files dictionary of the request object.
  3. Use the file's save() method to save the file to a location on the file system.

Implementing File Upload

Below is sample code for implementing file upload using Flask:

import os
from flask import Flask, flash, request, redirect, url_for, send_from_directory
from werkzeug.utils import secure_filename

UPLOAD_FOLDER = ‘/data/file_server/upload/’  # Path to store uploaded files
ALLOWED_EXTENSIONS = {‘txt’, ‘pdf’, ‘png’, ‘jpg’, ‘jpeg’, ‘gif’, ‘pcap’}  # Allowed file extensions for upload

app = Flask(name)
app.secret_key = ‘3ccfa213427578f707a015a87c5f94959df16cfd’
app.config[‘UPLOAD_FOLDER’] = UPLOAD_FOLDER

# Check if the file extension is valid
def allowed_file(filename):
    return ‘.’ in filename and 
    filename.rsplit(‘.’, 1)[1].lower() in ALLOWED_EXTENSIONS

# Upload the file and redirect the user to the URL of the uploaded file
@app.route(‘/’, methods=[‘GET’, ‘POST’])
def upload_file():
    if request.method == ‘POST’:
        # Check if the request contains a file part
        if ‘file’ not in request.files:
            flash(‘No file part’)
            return redirect(request.url)
        file = request.files[‘file’]
        # If the user does not select a file, the browser will still submit an empty file with no filename
        if file.filename == ‘’:
            flash(‘No selected file’)
            return redirect(request.url)
        if file and allowed_file(file.filename):
            filename = secure_filename(file.filename)
            file.save(os.path.join(app.config[‘UPLOAD_FOLDER’], filename))
            return redirect(url_for(‘uploaded_file’, filename=filename))
    return ‘’’

    <title>Upload new File</title>
    <h1>Upload new File</h1>
    <form method=post enctype=multipart form-data=“”>
        <input type=file name=file>
        <input type=submit value=upload>

    ‘’’

# Provide a download link for uploaded files
@app.route(‘/uploads/<filename>’)
def uploaded_file(filename):
    return send_from_directory(app.config[‘UPLOAD_FOLDER’], filename)

if name == ‘main’:
    from waitress import serve
    serve(app, host=“0.0.0.0”, port=9090)
</filename></input type=submit value=upload></input type=file name=file></form method=post enctype=multipart>

Security Considerations

When handling file uploads, security is an important consideration. For example, we do not want users to be able to upload any file type, as this could lead to cross-site scripting (XSS) attacks or other security issues. Therefore, the code logic adds a restriction on allowed file extension suffixes.
Additionally, the filename of uploaded files also requires consideration. The secure_filename() function is used to ensure filename security and prevent path traversal attacks. This function removes path information from the filename, leaving only the valid filename part. However, since the secure_filename function will omit Chinese characters when processing Chinese filenames, you should use English filenames.

Limiting Uploaded File Size

By default, Flask accepts file uploads of any size, but you can limit the maximum allowed file size by setting the MAX_CONTENT_LENGTH configuration key:

app.config['MAX_CONTENT_LENGTH'] = 16 * 1000 * 1000  # Limit to 16MB

This is generally used with an Nginx reverse proxy. You can control the maximum upload file size via Nginx's client_max_body_size directive.

Deploying the Application with Docker

Write the Dockerfile

FROM python:3

WORKDIR /app/fileserver

RUN pip install flask Werkzeug waitress –index-url http://mirrors.cloud.aliyuncs.com/pypi/simple/&nbsp;--trusted-host&nbsp;mirrors.cloud.aliyuncs.com

COPY fs.py ./

CMD [ “python”, “./fs.py” ]

Build the image

docker build -t flask-upload-app .

Start the container

docker run -d --name flask-upload-app -v /data/file_server/upload/:/data/file_server/upload/ -p 9090:9090 flask-upload-app

Configure Nginx reverse proxy

server {
    listen       31503 ;
    # Flask upload    
    location / {
        proxy_set_header Host $http_host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_pass http://localhost:9090/;
    }
}

Testing the Upload

Conclusion

Following the steps above, you can easily implement file upload functionality by writing a Flask application. I hope this is helpful to everyone. If you have any questions, feel free to leave a comment to discuss, or follow my WeChat Official Account O&M Piglet (运维小猪), thank you!

References:
https://flask.palletsprojects.com/en/2.3.x/patterns/fileuploads/


This is a discussion topic separated from the original thread at https://juejin.cn/post/7368767452614017059