In this episode, we’ll learn how to upload images to Cloudinary directly from a FastHTML app. We’ll build a simple image upload form, handle the form submission, and integrate Cloudinary’s Upload API to store the images.
Part 1: Understanding the Cloudinary Upload API
Overview of Cloudinary’s Upload Options:
Cloudinary offers several methods for uploading media files, including direct uploads from your server or client-side uploads with signed or unsigned credentials. In this episode, we will focus on server-side uploads using Python.
Key Parameters for Image Upload:
- file: The file to upload, which can be an image or any other supported file type.
- upload_preset: An optional parameter to specify a set of pre-defined upload options.
- timestamp: A Unix timestamp to prevent replay attacks.
- signature: A hash generated from the parameters and your API secret for security purposes.
- api_key: Your Cloudinary API key.
Part 2: Setting Up Cloudinary Integration in FastHTML
Step 1: Update Cloudinary Configuration
Ensure your config.py
file is correctly set up with Cloudinary credentials:
# config.py
import cloudinary
import cloudinary.uploader
import cloudinary.api
cloudinary.config(
cloud_name='your_cloud_name',
api_key='your_api_key',
api_secret='your_api_secret'
)
Step 2: Building the Upload Form in FastHTML
Let’s create a form in FastHTML to allow users to select and upload an image. Modify your main.py
file to include a route for the upload form:
# main.py
from fasthtml.common import *
import config # Import Cloudinary configuration
app, rt = fast_app()
@rt('/')
def get():
return Div(
P('Welcome to FastHTML with Cloudinary!'),
Form(
Input(type='file', name='file', accept='image/*'),
Button('Upload', type='submit'),
method='post',
action='/upload'
)
)
@rt('/upload', methods=['POST'])
def upload(request):
file = request.files['file']
response = cloudinary.uploader.upload(file.file)
image_url = response['secure_url']
return Div(
P('Image uploaded successfully!'),
Img(src=image_url, alt='Uploaded Image')
)
serve()
Explanation of Code:
- Form(…): Creates an HTML form with an input field for selecting files and a submit button.
- action=’/upload’: Specifies the route to handle form submission.
- @rt(‘/upload’, methods=[‘POST’]): Defines a new route to handle file uploads via POST requests.
- request.files[‘file’]: Retrieves the uploaded file from the request.
- cloudinary.uploader.upload(file.file): Uses Cloudinary’s SDK to upload the file to your Cloudinary account.
- response[‘secure_url’]: Extracts the URL of the uploaded image from the Cloudinary response.
Step 3: Run the Application
Run your FastHTML app with:
python main.py
Navigate to http://localhost:5001
in your browser. You should see the upload form. Select an image file and click the “Upload” button. If everything is set up correctly, the image will be uploaded to Cloudinary, and you’ll see it displayed on the page.
Part 3: Handling Errors and Improving User Experience
Error Handling:
To improve the user experience, we should handle potential errors, such as file type mismatches or upload failures. Update the upload
function in main.py
to include basic error handling:
@rt('/upload', methods=['POST'])
def upload(request):
try:
file = request.files['file']
if not file:
return Div(P('No file selected! Please choose a file to upload.'))
# Check for file type (optional, Cloudinary can handle many types)
if file.content_type not in ['image/jpeg', 'image/png', 'image/gif']:
return Div(P('Invalid file type! Please upload an image (JPEG, PNG, GIF).'))
# Upload file to Cloudinary
response = cloudinary.uploader.upload(file.file)
image_url = response['secure_url']
return Div(
P('Image uploaded successfully!'),
Img(src=image_url, alt='Uploaded Image')
)
except Exception as e:
return Div(P('An error occurred during upload: ' + str(e)))
Enhancing User Experience:
- Loading Indicators: Add a loading indicator to inform users that their file is being uploaded.
- Success and Error Messages: Display appropriate messages based on the upload outcome.
- Restricting File Size: Limit the maximum file size to ensure efficient uploads.
Example with Loading Indicator and Size Check:
# main.py
from fasthtml.common import *
import config # Import Cloudinary configuration
app, rt = fast_app()
@rt('/')
def get():
return Div(
P('Welcome to FastHTML with Cloudinary!'),
Form(
Input(type='file', name='file', accept='image/*'),
Button('Upload', type='submit', id='upload-btn'),
method='post',
action='/upload'
),
Script('''
document.getElementById('upload-btn').onclick = function() {
document.getElementById('upload-btn').innerHTML = 'Uploading...';
};
''')
)
@rt('/upload', methods=['POST'])
def upload(request):
try:
file = request.files['file']
if not file:
return Div(P('No file selected! Please choose a file to upload.'))
if file.content_length > 5 * 1024 * 1024: # 5 MB limit
return Div(P('File size exceeds limit! Please upload a file smaller than 5 MB.'))
if file.content_type not in ['image/jpeg', 'image/png', 'image/gif']:
return Div(P('Invalid file type! Please upload an image (JPEG, PNG, GIF).'))
response = cloudinary.uploader.upload(file.file)
image_url = response['secure_url']
return Div(
P('Image uploaded successfully!'),
Img(src=image_url, alt='Uploaded Image'),
Script('document.getElementById("upload-btn").innerHTML = "Upload";')
)
except Exception as e:
return Div(P('An error occurred during upload: ' + str(e)))
Conclusion
In this episode, you learned how to integrate Cloudinary’s Upload API with FastHTML, allowing you to upload images directly from your app. You also implemented basic error handling and user experience enhancements to improve the functionality and reliability of your application.
Homework/Assignment:
- Extend your upload functionality to support multiple file uploads.
- Implement a feature to display a list of uploaded images dynamically using Cloudinary’s API.
In the next episode, we’ll cover how to retrieve and display images from Cloudinary dynamically within your FastHTML application, creating a full-fledged media gallery.