FastAPI+React+MongoDB架构下二进制存储的产品图片无法显示问题求助
FastAPI+React+MongoDB架构下二进制存储的产品图片无法显示问题求助
我正在做一个个人项目,用FastAPI+React+MongoDB的技术栈,把产品图片以二进制形式存在MongoDB里。用户头像的同款功能已经完美跑通了,但产品图片就是显示不出来,现在遇到了UnicodeDecodeError的问题,实在找不到问题根源,求各位大佬帮忙看看!
技术栈
- FastAPI(后端)
- React(前端)
- MongoDB(数据库)
产品数据模型(FastAPI Pydantic)
from pydantic import BaseModel, Field, Optional from datetime import date from typing import List class Products(BaseModel): _id: str = Field(alias="_id") productName: str productID: str photographer: str productEvent: str productTags: Optional[List[str]] = None productPrice: float productCurrency: str productDate: Optional[date] = None productImage: Optional[bytes] = None productImageExtension: Optional[str] = None
前端产品上传组件(React)
import React, {useState, useEffect} from 'react'; import axios from 'axios'; function ProductUpload(){ const [productName, setProductName] = useState(''); const [productID, setProductID] = useState(''); const [photographer, setPhotographer] = useState(''); const [productEvent, setProductEvent] = useState(''); const [productTags, setProductTags] = useState([]); const [productPrice, setProductPrice] = useState(0); const [productCurrency, setProductCurrency] = useState(''); const [productDate, setProductDate] = useState(''); const [productImage, setProductImage] = useState(null); const [error, setError] = useState(''); const handleSubmit = async (event) => { event.preventDefault(); const formData = new FormData(); formData.append('productName', productName); formData.append('productID', productID); formData.append('photographer', photographer); formData.append('productEvent', productEvent); formData.append('productTags', JSON.stringify(productTags)); formData.append('productPrice', productPrice); formData.append('productCurrency', productCurrency); formData.append('productDate', productDate); formData.append('productImage', productImage); try { const response = await axios.post('http://localhost:8000/api/products', formData); console.log(response.data); } catch (error) { console.log(error.response.data); setError(error.response.data.detail); } }; return( <form onSubmit={handleSubmit} style={{alignContents: "center", justifyContent: "center", display: "flex", flexDirection: "column", margin: "auto", width: "100%"}}> <label> Product Name: <input type="text" value={productName} onChange={(event) => setProductName(event.target.value)} /> </label> <label> Product ID: <input type="text" value={productID} onChange={(event) => setProductID(event.target.value)} /> </label> <label> Photographer: <input type="text" value={photographer} onChange={(event) => setPhotographer(event.target.value)} /> </label> <label> Product Event: <input type="text" value={productEvent} onChange={(event) => setProductEvent(event.target.value)} /> </label> <label> Product Tags: <input type="text" value={productTags} onChange={(event) => setProductTags(event.target.value.split(','))} /> </label> <label> Product Price: <input type="number" value={productPrice} onChange={(event) => setProductPrice(event.target.value)} /> </label> <label> Product Currency: <input type="text" value={productCurrency} onChange={(event) => setProductCurrency(event.target.value)} /> </label> <label> Product Date: <input type="date" value={productDate} onChange={(event) => setProductDate(event.target.value)} /> </label> <label> Product Image: <input type="file" onChange={(event) => setProductImage(event.target.files[0])} /> </label> <button type="submit">Submit product</button> </form> ) } export default ProductUpload;
后端核心代码
main.py 接口部分
import io from datetime import datetime from fastapi import FastAPI, Form, File, UploadFile, HTTPException from fastapi.responses import StreamingResponse from pymongo import Binary from PIL import Image from your_module import Products, fetch_all_products, fetch_one_product, fetch_product_image, create_product, update_product, remove_product app = FastAPI() @app.get("/api/products") async def get_products(): response = await fetch_all_products() return response @app.get("/api/products{productName}", response_model=Products) async def get_product_by_name(productName): response = await fetch_one_product(productName) if response: return response raise HTTPException(404, f"There is no Product item with this title: {productName}") @app.get("/api/products/image/{productID}") async def get_product_img(productID: str): image_data, image_extension = await fetch_product_image(productID) if image_data: return StreamingResponse(io.BytesIO(image_data), media_type=f"image/{image_extension}") raise HTTPException(404, f"There is no Product with this ID: {productID}") @app.post("/api/products") async def post_product( productName: str = Form(...), productID: str = Form(...), photographer: str = Form(...), productEvent: str = Form(...), productTags: str = Form(...), productPrice: float = Form(...), productCurrency: str = Form(...), productDate: str = Form(...), productImage: UploadFile = File(...) ): productTagsList = [tag.strip() for tag in productTags.split(',')] productDateObj = datetime.strptime(productDate, "%Y-%m-%d").date() image_data = await productImage.read() img = Image.open(io.BytesIO(image_data)) original_format = img.format productImageExtension = original_format.lower() binary_image_data = Binary(image_data) product = Products( productName=productName, productID=productID, photographer=photographer, productEvent=productEvent, productTags=productTagsList, productPrice=productPrice, productCurrency=productCurrency, productDate=productDateObj, productImage=binary_image_data, productImageExtension=productImageExtension ) response = await create_product(product.dict()) if response: return response raise HTTPException(400, "Something went wrong / Bad Request") @app.put("/api/products{productName}/", response_model=Products) async def put_product(productName:str, productID:str, photographer:str, productEvent:str, productTags:List[str], productPrice:float, productCurrency: str, productDate: datetime.date, productImage: bytes): response = await update_product(productName, productID, photographer, productEvent, productTags, productPrice, productCurrency, productDate, productImage) if response: return response raise HTTPException(400, "Something went wrong / Bad Request") @app.delete("/api/products{productName}") async def delete_product(productName): response = await remove_product(productName) if response: return "Successfully deleted Product Item" raise HTTPException(400, "Something went wrong / Bad Request")
database.py 数据库操作部分
import datetime from pymongo import Binary from your_module import Products, database async def fetch_one_product(name): document = await database.products.find_one({"productName":name}) return document async def fetch_all_products(): productsL = [] cursor = database.products.find({}) async for document in cursor: productsL.append(Products(**document)) return productsL async def create_product(product: dict): product['productDate'] = datetime.datetime.combine(product['productDate'], datetime.datetime.min.time()) document = product result = await database.products.insert_one(document) document["_id"] = result.inserted_id product_instance = Products(**document) return product_instance.to_dict() async def update_product(productName, productID, photographer, productEvent, productTags, productPrice, productCurrency, productDate, productImage): await database.products.update_one({"name":productName},{"$set":{"productName": productName, "productID": productID, "photographer": photographer, "productEvent": productEvent, "productTags": productTags, "productPrice": productPrice, "productCurrency": productCurrency, "productDate": productDate, "productImage": productImage}}) document = await database.products.find_one({"name":productName}) return document async def remove_product(name): await database.products.delete_one({"name":name}) return True async def fetch_one_product_byID(productID): document = await database.products.find_one({"productID": productID}) return document async def fetch_product_image(productID: str): product = await database.products.find_one({"productID": productID}, {"productImage": 1, "productImageExtension": 1}) if product: image_data = bytes(product["productImage"]) image_extension = product["productImageExtension"] return image_data, image_extension return None, None
前端产品图片展示组件(React)
import React, { useState, useEffect } from "react"; import axios from "axios"; const ProductImage = ({ productID, size = "normal" }) => { const [image, setImage] = useState(null); let imageSize = "250px"; if(size === "small"){ imageSize = "20px"; } useEffect(() => { const fetchImage = async () => { try { const response = await axios.get(`http://localhost:8000/api/products/image/${productID}`, { responseType: "blob", }); setImage(URL.createObjectURL(response.data)); } catch (error) { console.error(error); } }; fetchImage(); }, [productID]); return <img src={image} alt={productID} style={{borderRadius: "50%", width: imageSize, height: imageSize}}/>; }; export default ProductImage;
遇到的错误
当访问展示产品图片的页面时,后端抛出以下错误:
File "pydantic\json.py", line 45, in pydantic.json.lambda UnicodeDecodeError: 'utf-8' codec can't decode byte 0x89 in position 0: invalid start byte
对比可正常工作的用户图片功能
用户头像的二进制存储和展示功能完全正常,以下是核心代码片段:
后端用户图片相关代码
@app.post("/api/User/image") async def upload_user_img(email: str = Form(...), image: UploadFile = File(...)): res = await user_image_upload(email, image) return res @app.get("/api/User/image/{email}") async def get_user_img(email: str): image_data = await fetch_user_image(email) if image_data: return StreamingResponse(io.BytesIO(image_data), media_type="image/png") raise HTTPException(404, f"There is no User with this email: {email}") async def user_image_upload(email: str, image: UploadFile = File(...)): user_email = email user = database.User.find_one({"email": user_email}) if user: image_data = await image.read() binary_image_data = Binary(image_data) database.User.update_one({"email": user_email}, {"$set": {"image": binary_image_data}}) return {"message": "Image uploaded successfully"} else: return {"message": "User not found"} async def fetch_user_image(email): user = await database.User.find_one({"email": email}, {"image": 1}) return user["image"]
前端用户图片展示组件
import React, { useState, useEffect } from "react"; import axios from "axios"; const UserImage = ({ email, size = "normal" }) => { const [image, setImage] = useState(null); let imageSize = "250px"; if(size === "small"){ imageSize = "20px"; } useEffect(() => { const fetchImage = async () => { try { const response = await axios.get(`http://localhost:8000/api/User/image/${email}`, { responseType: "blob", }); setImage(URL.createObjectURL(response.data)); } catch (error) { console.error(error); } }; fetchImage(); }, [email]); return <img src={image} alt={email} style={{borderRadius: "50%", width: imageSize, height: imageSize}}/>; }; export default UserImage;
用户数据模型
from pydantic import BaseModel, Optional from datetime import datetime class User(BaseModel): _id: str name: Optional[str] = None email: str phone: Optional[str] = None password: str active: Optional[bool] = None image: Optional[bytes] = None dob: Optional[datetime] = None weight: Optional[float] = None club: Optional[str] = None category: Optional[str] = None location: Optional[str] = None bank_details: Optional[bankDetails] = None isAdmin: Optional[bool] = None type: Optional[str] = None
备注:内容来源于stack exchange,提问作者sf001
相关产品推荐
相关产品推荐

