如何将含文本与文件的HTML表单数据单次请求发送至Flask并存入MongoDB
表单文本与图片一同上传至Flask并存入MongoDB的解决方案
问题描述
我需要上传包含文本字段和图片的表单数据,通过POST请求发送到Flask应用,并将文本和图片存储在同一个MongoDB集合中。之前尝试用axios.post()以application/json类型发送数据,但图片无法通过JSON格式传输;现在需要解决如何在同一次请求中同时发送表单文本条目和文件,以便在Flask中轻松检索并存储到数据库。
原代码展示
原前端JS代码
submit(e){ e.preventDefault(); axios.post('http://localhost:8103/api/db_create_post', { username: localStorage.getItem('username'), subject : this.state.subject, topic : this.state.topic, subtopic : this.state.subtopic, type: this.state.type, question : this.state.question, fact : this.state.fact, background : this.state.background, mcq1 : this.state.mcq1, mcq1opt1: this.state.mcq1opt1, mcq1opt2: this.state.mcq1opt2, mcq1opt3: this.state.mcq1opt3, mcq1opt4: this.state.mcq1opt4}, { "Content-Type": "multipart/form-data" }) .then(response => { console.log(response.data); this.setState({postCreated : true}); }) .catch((error) => { console.log(error) }); }
原前端HTML代码
<form autoComplete="on" className={style.formPost} method="post" id="formPost" enctype="multipart/form-data" onSubmit={this.submit}> <div id='page1'> <div className={style.formNavigation}><a href="#page1" className={style.linkNext}><BsCaretLeftFill/></a> <a href="#page2" className={style.linkNext} onClick={this.pageVisible2}><BsCaretRightFill/></a></div> <span className={style.addPostFormHeading} >Select Category</span> <input list="subjects" name="subject" placeholder="Subject" form="formPost" value={this.state.subject} onChange={this.handleChange} required/> <datalist id="subjects"> <option value="Computer Science"/> <option value="Science"/> <option value="Geography"/> <option value="Pharmacy"/> <option value="Accounts"/> <option value="Economics"/> <option value="Chemistry"/> <option value="Physics"/> </datalist> <input list="topics" name="topic" placeholder="Topic" form="formPost" value={this.state.topic} onChange={this.handleChange} required/> <datalist id="topics"> <option value="Computer Science"/> <option value="Science"/> <option value="Geography"/> <option value="Pharmacy"/> <option value="Accounts"/> <option value="Economics"/> <option value="Chemistry"/> <option value="Physics"/> </datalist> <input list="subTopics" name="subtopic" placeholder="Subtopic" form="formPost" value={this.state.subtopic} onChange={this.handleChange} required/> <datalist id="subTopics"> <option value="Computer Science"/> <option value="Science"/> <option value="Geography"/> <option value="Pharmacy"/> <option value="Accounts"/> <option value="Economics"/> <option value="Chemistry"/> <option value="Physics"/> </datalist> <input list="type" name="type" placeholder="Type" form="formPost" value={this.state.type} onChange={this.handleChange} required/> <datalist id="type"> <option value="News"/> <option value="Information"/> <option value="News & Information"/> </datalist> </div> <div id="page2"> <div className={style.formNavigation}><a href="#page1" className={style.linkNext} onClick={this.pageVisible1}><BsCaretLeftFill/></a> <a href="#page3" className={style.linkNext} onClick={this.pageVisible3}><BsCaretRightFill/></a></div> <span className={style.addPostFormHeading}>Create Post</span> <input type="text" form="formPost" name="question" placeholder="Question (optional)" size="50" value={this.state.question} onChange={this.handleChange}/> <textarea type="text" form="formPost" name="fact" placeholder="Fact (required)" maxLength="500" cols="50" rows="7" value={this.state.fact} onChange={this.handleChange} required/> </div> <div id="page3"> <div className={style.formNavigation}><a href="#page2" className={style.linkNext} onClick={this.pageVisible2}><BsCaretLeftFill/></a> <a href="#page4" className={style.linkNext} onClick={this.pageVisible4}><BsCaretRightFill/></a></div> <span className={style.addPostFormHeading}>Select Background</span> <span className={style.mediaUploadOptions}> <a href="#selectMedia" onClick={this.mediaUploadTypeSelectMedia}>Select Media</a> | <a href="#uploadMedia" onClick={this.mediaUploadTypeUploadMedia}>Upload Media</a> </span> <div className={style.selectMedia} id="selectMedia"> <form action="" id="searchBackground"> <input name="search" type="search" placeholder="Enter Background" form="searchBackground" /> <input type="submit" value="Search"/> </form> </div> <div className={style.uploadMedia} id="uploadMedia"> <input type="file" name="background" value={this.state.background} onChange={this.handleChange} form="formPost" accept="image/*" required/> </div> </div> <div id="page4"> <div className={style.formNavigation}><a href="#page3" className={style.linkNext} onClick={this.pageVisible3}><BsCaretLeftFill/></a> <a href="#page4" className={style.linkNext} onClick={this.pageVisible4}><BsCaretRightFill/></a></div> <input type="hidden" name="custId" value=""/> <input type="text" name="mcq1" placeholder="Ask a Question" form="formPost" size="50" value={this.state.mcq1} onChange={this.handleChange} required/> <input name="mcq1opt1" type="text" placeholder="Option 1" form="formPost" size="40" value={this.state.mcq1opt1} onChange={this.handleChange} required/> <input name="mcq1opt2" type="text" placeholder="Option 2" form="formPost" size="40" value={this.state.mcq1opt2} onChange={this.handleChange}required/> <input name="mcq1opt3" type="text" placeholder="Option 3" form="formPost" size="40" value={this.state.mcq1opt3} onChange={this.handleChange}/> <input name="mcq1opt4" type="text" placeholder="Option 4" form="formPost" size="40" value={this.state.mcq1opt4} onChange={this.handleChange}/> <input type="submit" value="Submit"/> </div> </form>
解决方案
1. 前端修改:用FormData封装并传输数据
JSON无法直接传输文件,必须使用FormData对象统一封装文本字段和文件,同时不要手动设置Content-Type,让浏览器自动生成正确的多部分表单请求头。
修改后的submit函数
submit(e){ e.preventDefault(); // 创建FormData对象 const formData = new FormData(); // 追加所有文本字段 formData.append('username', localStorage.getItem('username')); formData.append('subject', this.state.subject); formData.append('topic', this.state.topic); formData.append('subtopic', this.state.subtopic); formData.append('type', this.state.type); formData.append('question', this.state.question); formData.append('fact', this.state.fact); formData.append('mcq1', this.state.mcq1); formData.append('mcq1opt1', this.state.mcq1opt1); formData.append('mcq1opt2', this.state.mcq1opt2); formData.append('mcq1opt3', this.state.mcq1opt3); formData.append('mcq1opt4', this.state.mcq1opt4); // 追加图片文件(需提前在state中存储文件对象) if(this.state.backgroundFile){ formData.append('background', this.state.backgroundFile); } // 发送请求,不手动设置Content-Type axios.post('http://localhost:8103/api/db_create_post', formData) .then(response => { console.log(response.data); this.setState({postCreated : true}); }) .catch((error) => { console.log(error) }); }
修改handleChange函数以获取文件对象
文件输入无法通过value获取有效数据,需单独处理文件对象:
handleChange(e){ const { name, value, files } = e.target; if(files && files.length > 0){ // 处理文件输入,存储文件对象 this.setState({ [`${name}File`]: files[0] }); } else { // 处理文本输入,存储字段值 this.setState({ [name]: value }); } }
修改HTML文件输入标签
移除无效的value属性(文件输入的value不可手动设置):
<input type="file" name="background" onChange={this.handleChange} form="formPost" accept="image/*" required/>
2. Flask后端处理:接收数据并存入MongoDB
通过request.form获取文本字段,request.files获取上传的图片,将图片转为二进制数据后,和文本字段一同存入MongoDB集合。
Flask后端代码示例
from flask import Flask, request, jsonify from pymongo import MongoClient from bson.binary import Binary app = Flask(__name__) # 连接MongoDB(根据实际地址修改) client = MongoClient('mongodb://localhost:27017/') db = client['your_database_name'] collection = db['your_collection_name'] @app.route('/api/db_create_post', methods=['POST']) def create_post(): try: # 提取所有文本字段 post_info = { 'username': request.form.get('username'), 'subject': request.form.get('subject'), 'topic': request.form.get('topic'), 'subtopic': request.form.get('subtopic'), 'type': request.form.get('type'), 'question': request.form.get('question'), 'fact': request.form.get('fact'), 'mcq1': request.form.get('mcq1'), 'mcq1opt1': request.form.get('mcq1opt1'), 'mcq1opt2': request.form.get('mcq1opt2'), 'mcq1opt3': request.form.get('mcq1opt3'), 'mcq1opt4': request.form.get('mcq1opt4') } # 提取并处理图片文件 background_file = request.files.get('background') if not background_file: return jsonify({'error': '未上传图片'}), 400 # 将图片转为MongoDB支持的二进制格式 post_info['background'] = Binary(background_file.read()) # 插入MongoDB集合 result = collection.insert_one(post_info) return jsonify({ 'message': '帖子创建成功', 'post_id': str(result.inserted_id) }), 201 except Exception as e: return jsonify({'error': str(e)}), 500 if __name__ == '__main__': app.run(port=8103, debug=True)
补充说明
- 如果需要在前端直接展示图片,也可以将图片转为Base64字符串存储,前端获取后用
data:image/png;base64,...格式渲染 - 安装依赖:
pip install flask pymongo - 若存在跨域问题,需安装
flask-cors包并配置跨域支持
内容的提问来源于stack exchange,提问作者Akshat Jain
相关产品推荐
相关产品推荐

