React+Flask项目中使用BYTEA在PostgreSQL存储上传图片的方案咨询
React+Flask应用图片存入PostgreSQL BYTEA字段的实现方案
问题背景
我正在开发一款前端采用React、服务端使用Flask/Python的应用,需要支持用户上传图片并将图片存入PostgreSQL数据库,计划使用BYTEA类型存储图片,目前已有相关实现代码如下:
现有实现代码
1. 数据库表结构(SQLAlchemy定义)
class Items(db.Model): id = db.Column(db.Integer, primary_key=True) item_description = db.Column(db.String(640)) item_name = db.Column(db.String(60)) username = db.Column(db.String(120)) item_pic = db.Column(BYTEA) date = db.Column(db.Date, default=datetime.datetime.utcnow) price = db.Column(db.Float)
2. React端图片上传表单代码
<div className="flex text-sm text-gray-600"> <label htmlFor="file_upload" className="relative cursor-pointer bg-white rounded-md font-medium text-indigo-600 hover:text-indigo-500 focus-within:outline-none focus-within:ring-2 focus-within:ring-offset-2 focus-within:ring-indigo-500" > <span>Upload a file</span> <input id="file_upload" name="file_upload" type="file" className="sr-only" onChange={handleInputs} /> </label> <p className="pl-1">or drag and drop</p> </div>
注:文件类型的input出于浏览器安全限制不支持手动赋值,已删除原代码中无效的value={product.file_upload}属性
3. 原有前端请求代码(存在问题)
原有直接将文件对象放入JSON传输的写法无法正常传输二进制文件,需要修改。
// 原有错误写法,仅作展示 function onClickSave() { const requestData = { company_website: product.company_website, price: product.price, about: product.about, file_upload: product.file_upload, }; fetch('/save_product', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify(requestData), }) .then((response) => response.json()) .then((data) => { console.log(data); }); }
核心疑问解答
推荐在Flask服务端完成BYTEA格式转换,原因如下:
- JSON不支持直接传输二进制流,React端直接转码后传输体积会增加30%左右,性能更低
- SQLAlchemy原生支持将Python二进制对象直接映射到PostgreSQL的BYTEA字段,不需要额外转码操作
具体实现步骤
第一步:修改React端上传与请求逻辑
使用FormData格式传输文件和其他字段,不需要手动处理二进制转码:
- 首先修改文件选择的事件处理函数:
const handleInputs = (e) => { if (e.target.name === 'file_upload') { // 直接存储原始File对象到状态中 setProduct(prev => ({ ...prev, file_upload: e.target.files[0] })) return } // 其他普通字段的原有处理逻辑 setProduct(prev => ({ ...prev, [e.target.name]: e.target.value })) }
- 修改提交请求的逻辑:
function onClickSave() { const formData = new FormData() // 追加普通字段 formData.append('company_website', product.company_website) formData.append('price', product.price) formData.append('about', product.about) // 追加上传的文件 if (product.file_upload) { formData.append('file_upload', product.file_upload) } fetch('/save_product', { method: 'POST', // 不要手动设置Content-Type,浏览器会自动生成带边界标识的multipart/form-data类型 body: formData }) .then(res => res.json()) .then(data => console.log(data)) }
第二步:修改Flask端接口逻辑
直接读取上传文件的二进制内容,赋值给对应字段即可,SQLAlchemy会自动适配BYTEA类型:
from flask import request, jsonify @app.route('/save_product', methods=['POST']) def save_product(): # 读取FormData中的普通字段 company_website = request.form.get('company_website') price = request.form.get('price', type=float) about = request.form.get('about') # 读取上传的文件对象 upload_file = request.files.get('file_upload') # 初始化数据对象,其余字段根据你的业务逻辑补全 new_item = Items( item_description=about, price=price, # 其余字段自行补充赋值 ) # 写入图片二进制内容 if upload_file: new_item.item_pic = upload_file.read() db.session.add(new_item) db.session.commit() return jsonify({"status": "success", "item_id": new_item.id})
可选:图片回显处理
如果需要前端展示存储的图片,可在Flask查询后将二进制内容转Base64返回:
import base64 @app.route('/get_item/<int:item_id>') def get_item(item_id): item = Items.query.get_or_404(item_id) res = { "item_name": item.item_name, "price": item.price, "item_description": item.item_description } if item.item_pic: # 转为Base64格式的data URL,可直接放在img标签的src属性中展示 pic_b64 = base64.b64encode(item.item_pic).decode('utf-8') # 建议上传时同时存储文件的mime类型到数据库,此处直接对应使用更准确 res['item_pic'] = f'data:image/png;base64,{pic_b64}' return jsonify(res)
内容的提问来源于stack exchange,提问作者trufflewaffle
相关产品推荐
相关产品推荐

