Building a File Upload and Download API with Python and FastAPI
Introduction:
In this blog, we will explore how to create a file upload and download API using Python and FastAPI. FastAPI is a modern, fast, and highly efficient web framework for building APIs with Python. We will walk through the code provided and explain each step in detail, demonstrating how to handle file uploads, store them in Oracle Object Storage, and enable users to download the uploaded files.
Prerequisites: To follow along with this tutorial, you should have basic knowledge of Python, RESTful APIs, and web development concepts. Additionally, you’ll need Python and FastAPI installed on your system.
Setting Up the FastAPI Project: Before we dive into the code, make sure you have created a FastAPI project. If you haven’t, follow these steps:
- Install FastAPI and Uvicorn:
pip install fastapi uvicorn
2. Create a new Python file, e.g., main.py
, and import the required modules:
from fastapi import FastAPI, UploadFile, Form, HTTPAuthorizationCredentials, Security, Response, Request, Depends, status
from fastapi.responses import StreamingResponse
from sqlalchemy.orm import Session
import time
from datetime import datetime
import oci
from pydantic import BaseModel
Understanding the Code:
Now let’s go through the provided code and understand each part:
- Importing Modules and Initializing the FastAPI App:
from fastapi import FastAPI, UploadFile, Form, HTTPAuthorizationCredentials, Security, Response, Request, Depends, status
from fastapi.responses import StreamingResponse
from sqlalchemy.orm import Session
import time
from datetime import datetime
import oci
from pydantic import BaseModel
app = FastAPI()
Here, we import the necessary modules and create an instance of the FastAPI app.
- Defining the File Upload Endpoint:
@app.post("/file_uploading")
async def upload_file(
header_request: Request,
header_response: Response,
files: list[UploadFile],
fund_id: str = Form(...),
snap_shot_date: str = Form(...),
credentials: HTTPAuthorizationCredentials = Security(security),
db: Session = Depends(get_db),
):
# Function implementation
This endpoint handles file uploads. It expects multiple files, a fund ID, and a snapshot date as form data. Additionally, it requires HTTP authorization credentials for security and a database session.
- Validating Fund ID and Snapshot Date:
# Inside upload_file function
try:
fund_id = int(fund_id)
except Exception as e:
# Handle invalid Fund ID
header_response.status_code = status.HTTP_422_UNPROCESSABLE_ENTITY
response = await getResponse(x_transaction, True, "Invalid Fund ID")
return response
try:
date = datetime.strptime(snap_shot_date, '%Y-%m-%d')
if snap_shot_date == "":
# Handle missing Snapshot Date
header_response.status_code = status.HTTP_422_UNPROCESSABLE_ENTITY
response = await getResponse(x_transaction, True, "Fund ID not found")
return response
except Exception as e:
# Handle invalid date format
header_response.status_code = status.HTTP_422_UNPROCESSABLE_ENTITY
response = await getResponse(x_transaction, True, "Invalid Date Format")
return response
Here, we validate the fund ID and snapshot date provided in the form data. If the validation fails, we return appropriate error responses.
- Uploading Files to Oracle Object Storage:
# Inside upload_file function
configuration = {
"user": config("USER_OCID"),
"key_file": config("PRIVATE_KEY_FILE_PATH"),
"fingerprint": config("FINGER_PRINT"),
"tenancy": config("TENANCY"),
"region": config("REGION")
}
object_storage_client = oci.object_storage.ObjectStorageClient(configuration)
namespace = config("NAMESPACE")
bucket_name = config("BUCKET_NAME")
for file in files:
# Generate a unique filename and create the object name
fileName = file.filename.split(".")[0] + "_" + str(round(time.time() * 1000)) + "." + file.filename.split(".")[-1]
object_name = "inteliport_media/top_10_holding/" + str(datetime.now().date()) + "/" + fileName
# Upload the file to OCI object storage bucket
storing_path_in_Database = object_name.split("top_10_holding")[-1]
object_storage_client.put_object(namespace, bucket_name, object_name, file.file)
In this section, we set up the Oracle Object Storage client with appropriate configurations and credentials. We loop through the uploaded files, create unique filenames, and upload them to the specified bucket.
- Storing File Paths in the Database:
# Inside upload_file function
file_path_String = ""
for file in files:
# ... (Same loop as before)
file_path_String = file_path_String + storing_path_in_Database + ","
# ... (Rest of the function)
# Store the file paths in the database
for data_qc_top_10_holding in add_apth.all():
data_qc_top_10_holding.DOCUMENT_PATH = file_path_String
db.add(data_qc_top_10_holding)
db.commit()
response = await getResponse(x_transaction, True, "File Uploaded Successfully")
return response
Here, we concatenate the file paths and update the corresponding rows in the database with the file path information.
- Defining the File Download Endpoint:
@app.post("/download-file")
async def download_file(
response: Response, request: file_name_schema, header_request: Request, credentials: HTTPAuthorizationCredentials = Security(security), db: Session = Depends(get_db)
):
# Function implementation
This endpoint handles file downloads. It expects the file name to be downloaded and requires HTTP authorization credentials and a database session.
- Downloading Files from Oracle Object Storage:
# Inside download_file function
configuration = {
"user": config("USER_OCID"),
"key_file": config("PRIVATE_KEY_FILE_PATH"),
"fingerprint": config("FINGER_PRINT"),
"tenancy": config("TENANCY"),
"region": config("REGION")
}
x_transaction = header_request.headers.get("x-transaction-ref")
object_storage_client = oci.object_storage.ObjectStorageClient(configuration)
# Set up bucket information
namespace = config("NAMESPACE")
bucket_name = config("BUCKET_NAME")
object_name = "inteliport_media/top_10_holding" + request.file_name
# Download the file
try:
object = object_storage_client.get_object(namespace, bucket_name, object_name)
except:
# Handle file not found
response.status_code = status.HTTP_404_NOT_FOUND
return await getResponse(x_transaction, False, "File Not Found")
file_content = object.data.content
file = io.BytesIO(file_content)
response.headers["Content-Disposition"] = f"attachment; filename={object_name}"
response.headers["Content-Type"] = "application/octet-stream"
return StreamingResponse(file, media_type="application/octet-stream")
Conclusion:
Congratulations! You have successfully built a File Upload and Download API using FastAPI and Oracle Object Storage. This API allows users to upload files, stores them in the cloud, and then download them when needed. You can extend this API by adding more functionalities like file deletion, user authentication, and access control to fit your specific requirements. FastAPI’s easy-to-use and efficient framework, along with Oracle Object Storage’s scalable and secure cloud storage solution, makes this combination a powerful tool for building file management APIs in modern web applications.