I am using fastAPI to upload files through an endpoint and testing it using Postman. The endpoint is supposed to fetch the files and upload them to Firestore storage. However, the format of the files is getting stored as plain/text
. Here's my endpoint
import os
import uuid
from typing import Optional
import requests
from dotenv import load_dotenv
from fastapi import APIRouter
from fastapi import status as response_status
from fastapi.encoders import jsonable_encoder
from fastapi.responses import JSONResponse
from firebase_admin import credentials, firestore, initialize_app, storage
from starlette.datastructures import UploadFile
from starlette.requests import Request
@router.post("/saveEmailContent/")
async def save_email_content(request: Request):
# Get the email data from the request
form_data = await request.form()
# Get the sender, recipient, subject and body from the email
sender_email_id = form_data.get('sender')
recipient_email_id = form_data.get('recipient')
subject = form_data.get('subject')
email_body = form_data.get('body-plain')
attachment_count = form_data.get('attachment-count', 0)
to_update_attachment_count = attachment_count == 0
# Get the user and store ID from the recipient email ID
user_ref = firestore.client().collection('emailIds').where('emailId', '==', recipient_email_id).get()
if len(user_ref) == 0:
return JSONResponse(
status_code=response_status.HTTP_404_NOT_FOUND,
content={"message": "User not found"},
)
user_id = user_ref[0].to_dict().get('userId')
store_id = user_ref[0].to_dict().get('storeId')
# Add the email ID to the store document in the senderIds array
store_ref = firestore.client().collection('stores').document(store_id)
store_ref.update({'senderIds': firestore.ArrayUnion([sender_email_id]), "updatedOn": firestore.SERVER_TIMESTAMP})
# Upload attachments to Google Cloud Storage
attachment_urls = []
bucket_name = os.getenv("EMAIL_ATTACHMENT_STORAGE_BUCKET_NAME")
folder_name = os.getenv("EMAIL_ATTACHMENT_STORAGE_FOLDER_NAME")
for field_name, field_value in form_data.items():
if isinstance(field_value, UploadFile):
# Get the attachment from the request
attachment = await field_value.read()
# Update attachment count if update_flag is set to True
if to_update_attachment_count:
attachment_count += 1
# Generate a unique filename for each attachment
unique_filename = f"{folder_name}/{uuid.uuid4().hex}"
# Create a storage reference with the unique filename
storage_ref = storage.bucket(bucket_name).blob(unique_filename)
# Set the content type based on the original attachment's content type
storage_ref.content_type = field_value.content_type
print(f"Content-Type: {field_value.content_type}")
# Upload the attachment to Firebase Cloud Storage
storage_ref.upload_from_string(attachment)
# Get the public URL of the uploaded attachment
attachment_url = storage_ref.public_url
print(attachment_url)
# Add the attachment URL to the list
attachment_urls.append(attachment_url)
# Combine the data in a dictionary
email_data = {
"senderEmailId": sender_email_id,
"recipientEmailId": recipient_email_id,
"subject": subject,
"blurb": email_body[:25],
"recepientUserId": user_id,
"storeId": store_id,
"attachment_count": attachment_count,
}
# Add the email data to the database
db = firestore.client()
update_time, email_ref = db.collection('emails').add(email_data)
# Add the email content to the email document
email_ref.collection('content').add({
'body': email_body,
"attachments": attachment_urls,
})
email_data["attachments"] = attachment_urls
return JSONResponse(
status_code=response_status.HTTP_200_OK,
content={"message": "Email content saved successfully", "emailData": email_data},
)
My postman query looks like this
I am unable to figure out why the format is getting changed. When I explicitly try to change the format then I get the following error.
"errors": [
{
"message": "Content-Type specified in the upload (text/plain) does not match Content-Type specified in metadata (image/jpeg). If it was a simple upload (uploadType=media), the Content-Type was specified as a bare header. If it was a multipart upload (uploadType=multipart), then the Content-Type was specified in the second part of the multipart. If it was a resumable upload (uploadType=resumable), then the Content-Type was specified with the X-Upload-Content-Type header with the start of the resumable session. For more information, see https://cloud.google.com/storage/docs/json_api/v1/how-tos/upload.",
"domain": "global",
"reason": "invalid"
}
]
}
}
Any help with this?
I was able to solve it using the below method.
# Upload attachments to Google Cloud Storage
attachment_urls = []
bucket_name = os.getenv("EMAIL_ATTACHMENT_STORAGE_BUCKET_NAME")
folder_name = os.getenv("EMAIL_ATTACHMENT_STORAGE_FOLDER_NAME")
# Iterate through the form data to get the attachments
for field_name, field_value in form_data.items():
if isinstance(field_value, UploadFile):
# Get the attachment from the request as bytes
attachment_bytes = await field_value.read()
# Update attachment count if update_flag is set to True
if to_update_attachment_count:
attachment_count += 1
# Generate a unique filename for each attachment
unique_filename = f"{folder_name}/{uuid.uuid4().hex}"
# Create a storage reference with the unique filename
bucket = storage.bucket(bucket_name)
storage_ref = bucket.blob(unique_filename)
# Set the content type based on the original attachment's content type
content_type = field_value.content_type
if not content_type:
# If content type is not provided, try to guess it based on the filename
content_type, _ = mimetypes.guess_type(field_value.filename)
# Create a temporary file to store the attachment
with tempfile.NamedTemporaryFile(delete=True) as temp_file:
# Write the attachment bytes to the temporary file
temp_file.write(attachment_bytes)
temp_file.seek(0)
# Upload the attachment file to Firebase Cloud Storage
storage_ref.upload_from_file(temp_file, content_type=content_type)
# Make the uploaded object publicly accessible
storage_ref.make_public()
# Get the public URL of the uploaded attachment
attachment_url = storage_ref.public_url
# Add the attachment URL to the list
attachment_urls.append(attachment_url)