如何为调查问卷的每个问题添加评论功能与多文件上传功能?
实现调查问卷的单问题评论与多文件上传功能
前端实现思路
每个问卷问题模块需要单独挂载评论输入和文件上传组件,核心是把评论内容、文件列表与对应问题的ID绑定,通过FormData实现多类型数据的提交。
基础HTML结构
给每个问题包裹独立容器,包含评论输入框、多文件上传控件和提交按钮:
<!-- 单个问题模块 --> <div class="question-block" data-question-id="q1"> <h3>问题1:请描述您使用产品的场景</h3> <textarea class="question-comment" placeholder="写下您的补充评论..."></textarea> <input type="file" class="question-files" multiple accept=".pdf,.doc,.docx,.jpg,.png"> <button class="submit-comment-btn">提交评论与文件</button> </div>
JavaScript处理提交逻辑
监听按钮点击,收集当前问题的ID、评论内容和选中的文件,用FormData封装后发送请求:
document.querySelectorAll('.submit-comment-btn').forEach(btn => { btn.addEventListener('click', function() { const questionBlock = this.closest('.question-block'); const questionId = questionBlock.dataset.questionId; const commentContent = questionBlock.querySelector('.question-comment').value; const files = questionBlock.querySelector('.question-files').files; const formData = new FormData(); formData.append('question_id', questionId); formData.append('comment', commentContent); // 批量添加文件 for (let i = 0; i < files.length; i++) { formData.append('files[]', files[i]); } // 发送POST请求 fetch('/submit-question-comment', { method: 'POST', body: formData }) .then(res => res.json()) .then(data => { if (data.success) { alert('提交成功'); // 清空输入和文件选择 questionBlock.querySelector('.question-comment').value = ''; questionBlock.querySelector('.question-files').value = ''; } else { alert(`提交失败:${data.message}`); } }) .catch(err => console.error('提交出错:', err)); }); });
后端处理方案
后端需要接收FormData,解析数据后完成评论存储、文件校验与上传,同时建立评论、文件与问题的关联。
Node.js + Express示例
使用multer中间件处理文件上传:
const express = require('express'); const multer = require('multer'); const path = require('path'); const app = express(); // 配置文件存储路径和文件名 const storage = multer.diskStorage({ destination: function (req, file, cb) { cb(null, './uploads/'); // 确保uploads目录存在 }, filename: function (req, file, cb) { // 生成唯一文件名,避免覆盖 const uniqueSuffix = Date.now() + '-' + Math.round(Math.random() * 1E9); cb(null, file.fieldname + '-' + uniqueSuffix + path.extname(file.originalname)); } }); // 文件校验:限制类型和大小(5MB) const fileFilter = (req, file, cb) => { const allowedTypes = ['application/pdf', 'application/msword', 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', 'image/jpeg', 'image/png']; if (allowedTypes.includes(file.mimetype) && file.size <= 5 * 1024 * 1024) { cb(null, true); } else { cb(new Error('仅支持PDF、Word、JPG/PNG格式,单文件不超过5MB'), false); } }; const upload = multer({ storage: storage, fileFilter: fileFilter }); // 处理提交请求 app.post('/submit-question-comment', upload.array('files[]'), (req, res) => { try { const { question_id, comment } = req.body; const uploadedFiles = req.files.map(file => file.path); // 获取文件存储路径 // 1. 将评论内容存入数据库(示例逻辑,需替换为实际DB操作) // db.query('INSERT INTO question_comments (question_id, content) VALUES (?, ?)', [question_id, comment]); // 2. 将文件路径与评论关联存入数据库 // uploadedFiles.forEach(filePath => { // db.query('INSERT INTO comment_files (comment_id, file_path) VALUES (?, ?)', [newCommentId, filePath]); // }); res.json({ success: true, message: '评论与文件上传成功' }); } catch (err) { res.status(400).json({ success: false, message: err.message }); } }); app.listen(3000, () => console.log('服务器运行在端口3000'));
Python + Flask示例
使用原生request.files处理:
from flask import Flask, request, jsonify import os from werkzeug.utils import secure_filename app = Flask(__name__) app.config['UPLOAD_FOLDER'] = './uploads/' app.config['MAX_CONTENT_LENGTH'] = 5 * 1024 * 1024 # 5MB限制 ALLOWED_EXTENSIONS = {'pdf', 'doc', 'docx', 'jpg', 'jpeg', 'png'} def allowed_file(filename): return '.' in filename and filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS @app.route('/submit-question-comment', methods=['POST']) def submit_comment(): try: question_id = request.form.get('question_id') comment = request.form.get('comment') files = request.files.getlist('files[]') # 校验问题ID和评论(按需添加) if not question_id: return jsonify({'success': False, 'message': '缺少问题ID'}), 400 uploaded_files = [] for file in files: if file and allowed_file(file.filename): filename = secure_filename(file.filename) # 生成唯一文件名 unique_filename = f"{os.path.splitext(filename)[0]}_{os.urandom(8).hex()}{os.path.splitext(filename)[1]}" file_path = os.path.join(app.config['UPLOAD_FOLDER'], unique_filename) file.save(file_path) uploaded_files.append(file_path) # 存入数据库逻辑(示例) # comment_id = db.execute('INSERT INTO question_comments (question_id, content) VALUES (?, ?)', (question_id, comment)).lastrowid # for fp in uploaded_files: # db.execute('INSERT INTO comment_files (comment_id, file_path) VALUES (?, ?)', (comment_id, fp)) # db.commit() return jsonify({'success': True, 'message': '提交成功'}) except Exception as e: return jsonify({'success': False, 'message': str(e)}), 400 if __name__ == '__main__': os.makedirs(app.config['UPLOAD_FOLDER'], exist_ok=True) app.run(debug=True)
关键注意事项
- 数据关联:数据库需要设计三张核心表:
question(问卷问题)、question_comment(评论,关联question_id)、comment_file(文件,关联comment_id),确保评论和文件能追溯到对应问题。 - 前端体验优化:添加文件上传进度提示、已选文件预览、格式/大小校验提示,提升用户体验。
- 安全防护:后端必须二次校验文件类型和大小,避免恶意文件上传;对文件名进行转义处理,防止路径遍历攻击;存储文件时使用随机文件名,避免冲突。
- 存储方案:如果问卷流量大,建议将文件存储到云对象存储,而非本地服务器,减轻服务器压力并提升访问速度。
内容的提问来源于stack exchange,提问作者Mahdi
相关产品推荐
相关产品推荐

