Image Uploads with Google Cloud Storage and Flask
The ability to upload files, especially images, to a server is an essential feature for many web applications. From profile photos to document storage, providing users with the ability to upload files can significantly enhance the user experience. This article will walk you through the process of adding a file upload functionality to a Python backend exposed via a Flask API.
Overview
The code snippet provided showcases a Flask route (/upload-image
) that allows for image uploads. Once an image is uploaded, it's temporarily saved to the server's file system. Following this, it's uploaded to Google Cloud Storage (GCS) and made publicly accessible. The public URL of the uploaded image is then returned as the API response.
API Code
@app.route('/upload-image', methods=['POST'])
def upload_image():
file = request.files.get('image')
if not file:
return jsonify({'error': 'Image is required'}), 400,
tmp_file = f'/tmp/{file.filename}'
file.save(tmp_file)
url = gcs_upload_image(tmp_file)
os.remove(tmp_file)
return jsonify({'url': url})
Let's break down the process step-by-step:
1. Flask Route for File Upload
The route /upload-image
is defined to accept POST
requests:
@app.route('/upload-image', methods=['POST'])
def upload_image():
...
This function starts by checking if there's a file attached with the key 'image'. If not, an error is returned
file = request.files.get('image')
if not file:
return jsonify({'error': 'Image is required'}), 400,
2. Saving the Image Temporarily
After validating the presence of the uploaded file, it's temporarily saved to the server's /tmp
directory:
tmp_file = f'/tmp/{file.filename}'
file.save(tmp_file)
The use of the /tmp
directory ensures that the file is not stored permanently on the server, as it's meant to be a short-lived location.
Uploading the Image to Google Cloud Storage
With the image saved temporarily, the next step is to upload it to GCS:
url = gcs_upload_image(tmp_file)
The upload_image
function is responsible for interfacing with GCS:
def gcs_upload_image(filename: str):
storage_client: storage.Client = storage.Client()
bucket: storage.Bucket = storage_client.bucket(GCS_ASSETS_BUCKET)
blob: storage.Blob = bucket.blob(filename.split("/")[-1])
blob.upload_from_filename(filename)
blob.make_public()
public_url: str = blob.public_url
print(f"Image uploaded to {public_url}")
os.remove(filename)
return public_url
Here's a breakdown of the function:
- A connection is established with GCS using the
storage.Client()
. - The desired bucket (specified by
GCS_ASSETS_BUCKET
) is accessed. - A new blob (GCS's term for an object) is created with the filename.
- The image is uploaded using
upload_from_filename
. - The blob is made public so anyone can access the image via its URL.
- The temporary file is deleted from the server using
os.remove
. - The public URL of the blob is returned.
Returning the Public URL
Finally, the public URL of the uploaded image is returned as the API response:pythonCopy codereturn jsonify({'url': url})
Considerations
While the provided code offers a clear and straightforward way to upload files to GCS, there are a few important considerations:
- Security: It's essential to validate and sanitize uploaded files to prevent potential security threats. This can include checking the file's MIME type or using libraries to ensure the file is a genuine image.
- File Naming: To prevent files from overwriting each other, consider generating unique filenames before uploading them to GCS.
- Error Handling: Always account for potential errors during the file upload process, such as network failures, GCS outages, or quota restrictions.
- Cleaning Temporary Files: Ensure that temporary files are deleted even if the upload to GCS fails. This prevents the server's storage from filling up unnecessarily.
Conclusion
Incorporating file upload functionality into a Flask API allows for a richer user experience and paves the way for various features in web applications. While the process of uploading images to GCS is relatively straightforward, always prioritize security and error handling to ensure a smooth and safe user experience.