如何用HTML input标签实现多图片或单视频上传限制
多图/单视频发帖上传功能实现示例
前端实现(HTML + JavaScript)
核心是在用户选择文件时做前置校验,确保只能选多张图片或单个视频,不能混合选择。
HTML 结构
<form id="postForm" enctype="multipart/form-data"> <input type="file" id="mediaInput" multiple accept="image/*,video/*"> <button type="submit">发布帖子</button> <div id="errorMsg" style="color:red;"></div> </form>
JavaScript 校验与提交逻辑
const mediaInput = document.getElementById('mediaInput'); const postForm = document.getElementById('postForm'); const errorMsg = document.getElementById('errorMsg'); // 监听文件选择事件,实时校验 mediaInput.addEventListener('change', (e) => { const files = Array.from(e.target.files); let hasImage = false; let hasVideo = false; let videoCount = 0; // 遍历文件判断类型 files.forEach(file => { if (file.type.startsWith('image/')) { hasImage = true; } else if (file.type.startsWith('video/')) { hasVideo = true; videoCount++; } }); // 校验规则:不能同时有图和视频,视频最多1个,图片最多9个(可调整) if (hasImage && hasVideo) { errorMsg.textContent = '不能同时上传图片和视频,请选择其中一种'; mediaInput.value = ''; // 清空选择 } else if (videoCount > 1) { errorMsg.textContent = '最多只能上传1个视频'; mediaInput.value = ''; } else if (hasImage && files.length > 9) { errorMsg.textContent = '最多只能上传9张图片'; mediaInput.value = ''; } else { errorMsg.textContent = ''; } }); // 表单提交 postForm.addEventListener('submit', async (e) => { e.preventDefault(); const files = mediaInput.files; if (files.length === 0) { errorMsg.textContent = '请选择要上传的媒体文件'; return; } const formData = new FormData(); // 添加帖子内容(如果有的话) formData.append('content', '这是我的帖子内容'); // 添加所有媒体文件 Array.from(files).forEach(file => { formData.append('media', file); }); try { const response = await fetch('/api/posts', { method: 'POST', body: formData }); if (response.ok) { alert('帖子发布成功'); postForm.reset(); } else { const err = await response.json(); errorMsg.textContent = err.message; } } catch (err) { errorMsg.textContent = '网络错误,请重试'; } });
后端实现(Node.js + Express + Multer)
负责接收文件,再次校验类型和数量,然后保存到服务器或云存储。
依赖安装
npm install express multer
核心代码
const express = require('express'); const multer = require('multer'); const fs = require('fs'); const app = express(); // 配置文件存储路径和文件名 const storage = multer.diskStorage({ destination: (req, file, cb) => { const uploadDir = './uploads/'; if (!fs.existsSync(uploadDir)) { fs.mkdirSync(uploadDir); } cb(null, uploadDir); }, filename: (req, file, cb) => { const uniqueSuffix = Date.now() + '-' + Math.round(Math.random() * 1E9); cb(null, `${file.fieldname}-${uniqueSuffix}.${file.originalname.split('.').pop()}`); } }); // 文件过滤:只允许图片和视频 const fileFilter = (req, file, cb) => { if (file.type.startsWith('image/') || file.type.startsWith('video/')) { cb(null, true); } else { cb(new Error('只允许上传图片或视频'), false); } }; const upload = multer({ storage: storage, fileFilter: fileFilter, limits: { fileSize: 100 * 1024 * 1024 } // 限制单文件100MB(可调整) }); // 处理发帖请求 app.post('/api/posts', upload.array('media', 9), (req, res) => { const files = req.files; let hasImage = false; let hasVideo = false; let videoCount = 0; // 后端二次校验,防止前端绕过 files.forEach(file => { if (file.mimetype.startsWith('image/')) { hasImage = true; } else if (file.mimetype.startsWith('video/')) { hasVideo = true; videoCount++; } }); if (hasImage && hasVideo) { // 删除已上传的文件 files.forEach(file => fs.unlinkSync(file.path)); return res.status(400).json({ message: '不能同时上传图片和视频' }); } if (videoCount > 1) { files.forEach(file => fs.unlinkSync(file.path)); return res.status(400).json({ message: '最多只能上传1个视频' }); } // 整理帖子数据,可存入数据库 const postData = { content: req.body.content, media: files.map(file => ({ url: `/uploads/${file.filename}`, type: file.mimetype.startsWith('image/') ? 'image' : 'video' })) }; console.log('保存帖子数据:', postData); res.status(200).json({ message: '帖子发布成功', post: postData }); }); app.listen(3000, () => { console.log('服务器运行在端口3000'); });
关键逻辑说明
- 前端校验:用户选择文件时即时判断类型和数量,给出反馈,减少无效请求。
- 后端二次校验:必须做这一步,避免前端校验被绕过,确保数据合法性。
- 文件处理:用Multer管理多文件上传,自动处理存储路径和文件名,避免重名。
- 错误回滚:校验不通过时删除已上传文件,避免占用服务器空间。
内容的提问来源于stack exchange,提问作者SupraCoder
相关产品推荐
相关产品推荐

