Cloudinary Blog

Creating an API With Python Flask to Upload Files to Cloudinary

Rebecca Peltz
By Rebecca Peltz
Creating an API With Python Flask to Upload Files to Cloudinary

Code

Cloudinary offers SDKs for many programming languages and frameworks. Even though it also offers an Upload API endpoint for both back-end and front-end code, most developers find the SDKs very helpful. If you're working with a powerful back-end framework like Python Flask, you'll be happy to hear that a Python SDK is now available.
This tutorial walks you through the process of building an API to upload images to Cloudinary. You can also upload other file types, including video and even nonmedia files, with the API.

Understanding the Difference Between Back End and Front End

Generally, code that runs on the server is back end, and code that runs on the browser is front end. However, since server code can render HTML, CSS, and JavaScript, which all run on the browser, confusion sometimes results. In the context of the Cloudinary SDKs, the back-end ones can read secret credentials, which should not be shared in the front end. In other words, you must never expose the back-end environment variables in the front end because front-end SDKs cannot hide credentials that are meant to be kept secret. As a solution, you can upload browser code with Cloudinary’s unsigned presets without revealing secret credentials. A better way would be to build a back-end API for secure uploads, keeping your API_SECRET credential hidden. Read on to see how to do that with Cloudinary’s Python SDK and Python Flask or Python Django.

Coding a Flask API to Upload to Cloudinary

The Flask framework makes it easy to define routes and their functionalities. To get started, first create a route named /upload, which accepts a POST that contains multipart/form-data. You then package up the image file input into a FormData object in a submit handler and POST it to your own Flask API to call Cloudinary’s Upload API, which is configured with your full set of Cloudinary credentials.

Flask's request option enables you to get data from the client. When submitting files, such as uploading images, you can call upon request to access them.

Copy to clipboard
from flask import Flask,render_template, request
if request.method == 'POST':
    file_to_upload = request.files['file']

The data retrieved from request.files['file'] is an instance of werkzeug.FileStorage. The object can be handed off to the Python SDK’s Upload function. Flask then wraps Werkzeug, which handles the details of the Web Server Gateway Interface (WSGI).

Copy to clipboard
if file_to_upload:
    upload_result = cloudinary.uploader.upload(file_to_upload)
    app.logger.info(upload_result)

For uploads to Cloudinary, the default resource_type is image. To expand or create a new Flask API, add resource_type: 'video' or resource_type: 'raw' for video or raw files, respectively. Raw refers to nonmedia file formats, including text and JSON.

Finally, upload_result is an object that contains the upload response. To complete the actions of your upload API, return that response to the client, like this:

Copy to clipboard
from flask import jsonify
return jsonify(upload_result)

Below is the complete code of your upload API.

Copy to clipboard
@app.route("/upload", methods=['POST'])
def upload_file():
  app.logger.info('in upload route')

  cloudinary.config(cloud_name = os.getenv('CLOUD_NAME'), api_key=os.getenv('API_KEY'), 
    api_secret=os.getenv('API_SECRET'))
  upload_result = None
  if request.method == 'POST':
    file_to_upload = request.files['file']
    app.logger.info('%s file_to_upload', file_to_upload)
    if file_to_upload:
      upload_result = cloudinary.uploader.upload(file_to_upload)
      app.logger.info(upload_result)
      return jsonify(upload_result)

Sign up for Cloudinary free today!


Setting Up the Flask App

Next, follow the steps below to build the app.

Set Up the Virtual Environment

The command below establishes your virtual environment. This is an important step for encapsulating the libraries you'll be using in this app.

Copy to clipboard
python3 -m venv env
source env/bin/activate

To deactivate the environment, type:

Copy to clipboard
deactivate
source env/bin/activate

Install Flask

Install Flask with this command:

Copy to clipboard
python3 -m pip install Flask==1.1.1

Add a requirements.txt File

Create a requirements.txt file to keep track of all the versioned libraries you need for the app and to facilitate future deployment.

Copy to clipboard
python3 -m pip freeze > requirements.txt

Upgrade the Python Package Installer

If necessary, upgrade the Python package installer, called PIP, with the command below. The command might vary, depending on your local Python installation. As shown below, the freeze command writes the library and version to the requirements.txt file.

Copy to clipboard
usr/local/opt/python@3.9/bin/python3.9 -m pip install --upgrade pip
python3 -m pip freeze > requirements.txt

Install Cloudinary

Next, install Cloudinary to gain access to its Upload API for the Python SDK.

Copy to clipboard
 python3 -m pip install cloudinary
 python3 -m pip freeze > requirements.txt

Add CORS support

If you want to access your upload API from a client served from a different host, add Cross Origin Resource Sharing (CORS) support:

Copy to clipboard
python3 -m pip install flask-cors
python3 -m pip freeze > requirements.txt

Now you can add code to configure CORS for all the APIs and, specifically, the upload API. The code below demonstrates both options.

Copy to clipboard
from flask_cors import CORS, cross_origin
app = Flask(__name__)
# After creating the Flask app, you can make all APIs allow cross-origin access.
CORS(app)
# or a specific API
@app.route("/upload", methods=['POST'])
@cross_origin()

Work With Environment Variables

You can easily load environment variables with Python. Conveniently, the python-dotenv library is modeled on the Node.js dotenv package. You need three Cloudinary environment variables, i.e., CLOUD_NAME, API_KEY, and API_SECRET, for your code, but don't share API_SECRET. You can export those variables to the local environment. When you deploy to Heroku, you can add them to make them available to the app when it runs in a Heroku container.

Copy to clipboard
from dotenv import load_dotenv
load_dotenv()

In your upload API, you read in the environment variables with the cloudinary.config() function. You then access those variables with the os.getenv() function in the dotenv library.

Copy to clipboard
cloudinary.config(cloud_name = os.getenv('CLOUD_NAME'), api_key=os.getenv('API_KEY'), 
    api_secret=os.getenv('API_SECRET'))

When working locally, you can create a gitignore'd .env file that contains your Cloudinary credentials for local testing. The values for those credentials are displayed in your Cloudinary console.

Copy to clipboard
CLOUD_NAME=CLOUD_NAME
API_KEY=API_KEY
API_SECRET=API_SECRET

Next, install the python-dotenv library:

Copy to clipboard
python3 -m pip install python-dotenv
python3 -m pip freeze > requirements.txt

Testing the API Locally

You're now ready to test the app locally. You can do end-to-end testing with Postman and a local form that points at a server running on localhost.

Create your app with an upload function by downloading, cloning, or copying from this GitHub repository. Add your credentials to a .env file and then start your server on localhost.

Copy to clipboard
python3 app.py

The above command opens a server at http://127.0.0.1:5000/. Note that the default port is 5000.

Test With Postman

In Postman, do the following:

  1. Set the method to POST.
  2. Set the body type to form-data.
  3. Establish Key/Value pairs for input. Set the value for the Key to file. Mouse over the Key field and choose Text or File from the drop-down menu. Under File is a Select Files button; click it to select a file from your local drive.
  4. Click Send to process the request.

The Cloudinary Upload API response is displayed at the bottom of the page

Localhost with Postman

Test With a Local Form

Follow these steps:

  1. Open the index.html file with a local HTTP server. Choose a local file and click Upload.
  2. Run the JavaScript code below. The fetch command calls the server that runs on localhost.

    Copy to clipboard
    fetch("http://127.0.0.1:5000/upload", options)
    .then((response) => {
    return response.json();
    })
    .then((data) => {
    console.log(data);
    })
    .catch((error) => {
    console.log(error);
    });

    If the process is successful, the Cloudinary Upload API response is displayed in the console. Don't forget to set event.preventDefault(); for the submit handler. Otherwise, you'll get a page refresh and won't see your log.

  3. Post a file in a submit handler by creating a FormData object and appending the file, as shown in the code below.

    Copy to clipboard
    const formData = new FormData();
    formData.append("file", fileInput.files[0]);
    const options = {
    method: "POST",
    body: formData,
    };

The above code packages up the file you select in your form input as if you had submitted a multipart form. A multipart form is important for submitting files because each input is sent as a block of data, separated by boundaries. The result is then displayed in your console, like this:

local form

Deploying the Flask App to Heroku

Heroku is a platform as a service (PaaS) for serving prototypes or production-ready apps. Host the API you just built on Heroku by doing the following:

  1. Create a free account on Heroku.

  2. Install Gunicorn, short for Green Unicorn, a Python WSGI HTTP server for hosting apps.

    Copy to clipboard
    python3 -m pip install gunicorn==20.0.4
    python3 -m pip freeze > requirements.txt
  3. Create a file called Prodfile with instructions for Heroku to start your app with Gunicorn:

    Copy to clipboard
    web: gunicorn app:app
  4. Create a file called runtime.txt that contains the Python version you want to use:

    Copy to clipboard
    python-3.9.1
  5. Deploy with Git:

Copy to clipboard
git init
git add .
git commit -m"my first commit"
# Create a new Heroku app
heroku create
# You will get a URL where the app will be hosted
git remote -v
# will confirm that you have set up a remote to push to Heroku

# If you have an existing Heroku app that you created in the Heroku GUI
# you can add it with this command instead of creating a new one
heroku git:remote -a thawing-inlet-61413

# To deploy, you can now just push to Heroku
git push heroku master

# Open your server (it's nice to have a GET method so you can verify like this)
heroku open

# To log in to Heroku from the command line, type:
heroku login
# You’ll then be prompted to open the webpage to log in

You can build the new Heroku app with the CLI. However, as noted above, if you built it on the heroku.com dashboard, you can just add the link Heroku created for you to a git:remote command.

Loading Environment Variables on Heroku

To load environment variables on Heroku, open the dashboard and navigate to your new server instance. Click the Settings tab and scroll down to the Config Vars section. Click Reveal Config Vars, where you can enter Key/Value pairs. This is where you load your Cloudinary environment variables: CLOUD_NAME, API_KEY, and API_SECRET.

Heroku Config Vars

For more options for setting up prod and dev instances, see the Heroku documentation.

Testing the API Deployed on Heroku

If you have a GET method API like the "Hello World!" in the app.py example, you can open the Heroku app in the browser.

For end-to-end testing, you can POST to the Heroku server link from Postman. The result is similar to that for local testing.

Remote Post

Alternatively, modify the index.html file to post to your Heroku server. Just add your Heroku server link to the fetch command, like this:

Copy to clipboard
fetch("https://xxxx.herokuapp.com", options)

Securing the App

You might have noticed that the Heroku deployment link has been erased from the images above. That’s for security. Deploying this app to Heroku enables you to upload data to Cloudinary. If the link is not secured for use on an authenticated webpage only, anyone can upload to your Cloudinary cloud. Even though you are hiding your environment variables, you’d have created a public method to change data on your cloud.

This tutorial does not cover how to set up authentication. Many options are available for authentication of API routes. You might want to look into Auth0, Google, or GitHub. Alternatively, build your own process based on your user database for this type of authentication.

Moving On

Once you get this API running, you can build other media APIs with the Cloudinary Python SDK by following the same pattern. Have fun!

Recent Blog Posts

Auto-Tag Videos With Cloudinary: A Tutorial

Video is one of the best ways for capturing audience attention. Product demonstrations, property tours, tutorials, and keynotes all make great content—provided that they’re readily searchable by your team and visitors alike. Even though tagging and categorization help organize videos and make them easy to find, the labeling process is labor intensive, taking time away from key tasks like creating new videos.

Read more
Video Uploads With Cloudinary

Setting up the infrastructure for video uploads can go from straightforward to complex real fast. Why? Because many challenges are involved in building a foolproof service for an efficient and smooth process.

Read more
Optimize Media to Get Ready for Google’s Core Web Vitals

For years, Google has been updating its search algorithm to prioritize end-user experience, displaying the most relevant and helpful content at the top of search results. The latest—and maybe the most significant—update so far is Core Web Vitals (CWVs), which are new metrics announced a year ago that will, starting in June, begin determining search rankings. With this update, Google is being abundantly clear that visual experience of webpages is paramount.

Read more
Compressing, Resizing, and Optimizing Videos in Laravel

Videos are large media files—in most cases, at least four times larger than images—and are often created for ads, marketing campaigns, and instructional content to convey as much information as possible in a short time. Ensuring that videos do not buffer all the time and that the user’s data is protected from rapid consumption due to heavy page weight must be the modus operandi for all website builders and e-business owners.

Read more
Five Ways to Effectively Manage Online Media

The digital economy is driven by highly visual experiences. After all, viewers process images 60,000 times faster than text. Therefore, it’s no surprise that top-notch visual media has been an essential component of a captivating e-commerce experience for years. Nor is it surprising that visual media’s importance only rose during the COVID-19 pandemic, a reality for all retailers, including our client Levi’s.

Read more
How to Use the Cloudinary Media Editor Widget

At Cloudinary, we manage the entire pipeline of media assets for thousands of customers of varying sizes from numerous verticals.

As part of our commitment to support the entire flow of media assets, we are now introducing an intuitive media editing widget: an out­-of­-the-­box, interactive UI providing your users with a set of common image editing actions for immediate use on your website or web app. The widget is interactive and simple, built on Cloudinary's transformation capabilities, and requiring only a few lines of code to integrate. Afterwards, you can seamlessly and effortlessly add content to your site or app with no need for in-house image editing capabilities.

Read more