You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何将含文本与文件的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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.24 08:15:30