英文:
File Format is automatically changing to plain/text
问题
我正在使用fastAPI通过一个端点上传文件,并使用Postman进行测试。该端点应该获取文件并将其上传到Firestore存储。然而,文件的格式被存储为plain/text
。以下是我的端点:
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):
# 获取请求中的电子邮件数据
form_data = await request.form()
# 从电子邮件中获取发送者、接收者、主题和正文
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
# 从接收者电子邮件ID获取用户和存储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')
# 将电子邮件ID添加到senderIds数组中的存储文档中
store_ref = firestore.client().collection('stores').document(store_id)
store_ref.update({'senderIds': firestore.ArrayUnion([sender_email_id]), "updatedOn": firestore.SERVER_TIMESTAMP})
# 将附件上传到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):
# 从请求中获取附件
attachment = await field_value.read()
# 如果update_flag设置为True,则更新附件计数
if to_update_attachment_count:
attachment_count += 1
# 为每个附件生成一个唯一的文件名
unique_filename = f"{folder_name}/{uuid.uuid4().hex}"
# 使用唯一的文件名创建存储引用
storage_ref = storage.bucket(bucket_name).blob(unique_filename)
# 根据原始附件的内容类型设置内容类型
storage_ref.content_type = field_value.content_type
print(f"Content-Type: {field_value.content_type}")
# 将附件上传到Firebase Cloud Storage
storage_ref.upload_from_string(attachment)
# 获取上传附件的公共URL
attachment_url = storage_ref.public_url
print(attachment_url)
# 将附件URL添加到列表中
attachment_urls.append(attachment_url)
# 将数据组合在一个字典中
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,
}
# 将电子邮件数据添加到数据库
db = firestore.client()
update_time, email_ref = db.collection('emails').add(email_data)
# 将电子邮件内容添加到电子邮件文档中
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},
)
我的Postman查询如下图所示。
我无法弄清楚为什么格式会改变。当我尝试显式更改格式时,我得到以下错误。
"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"
}
]
}
有关此问题的任何帮助?
英文:
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?
答案1
得分: 0
我能够使用以下方法解决它。
# 上传附件到 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):
# 从请求中获取附件的字节数据
attachment_bytes = await field_value.read()
# 如果设置 to_update_attachment_count 为 True,则更新附件计数
if to_update_attachment_count:
attachment_count += 1
# 为每个附件生成唯一的文件名
unique_filename = f"{folder_name}/{uuid.uuid4().hex}"
# 使用唯一的文件名创建存储引用
bucket = storage.bucket(bucket_name)
storage_ref = bucket.blob(unique_filename)
# 根据原始附件的内容类型设置内容类型
content_type = field_value.content_type
if not content_type:
# 如果未提供内容类型,尝试根据文件名猜测它
content_type, _ = mimetypes.guess_type(field_value.filename)
# 创建一个临时文件来存储附件
with tempfile.NamedTemporaryFile(delete=True) as temp_file:
# 将附件字节写入临时文件
temp_file.write(attachment_bytes)
temp_file.seek(0)
# 将附件文件上传到 Firebase Cloud Storage
storage_ref.upload_from_file(temp_file, content_type=content_type)
# 使上传的对象公开可访问
storage_ref.make_public()
# 获取上传的附件的公共 URL
attachment_url = storage_ref.public_url
# 将附件 URL 添加到列表中
attachment_urls.append(attachment_url)
英文:
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)
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论