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

如何用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');
});

关键逻辑说明

  1. 前端校验:用户选择文件时即时判断类型和数量,给出反馈,减少无效请求。
  2. 后端二次校验:必须做这一步,避免前端校验被绕过,确保数据合法性。
  3. 文件处理:用Multer管理多文件上传,自动处理存储路径和文件名,避免重名。
  4. 错误回滚:校验不通过时删除已上传文件,避免占用服务器空间。

内容的提问来源于stack exchange,提问作者SupraCoder

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.19 07:15:33