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

如何通过POST请求为HTML5播放器提供视频资源?

实现POST请求无刷新切换视频的完整方案

看起来你已经走对了方向——改成POST请求、引入body-parser都是正确的第一步!不过我注意到你代码里有个小笔误:var path = 'D:/VideoDirectory/'+ folde...这里的folde少了个r,应该是folder,这可能会导致路径错误哦。

接下来我给你一套完整的可运行方案,涵盖后端Express配置、POST接口实现,以及前端无刷新切换视频的代码:

后端Express代码完善

首先确保你的body-parser配置正确,然后完善POST接口,还要处理视频流的返回(因为播放视频需要流式传输,不能直接返回文件):

const express = require('express');
const app = express();
const fs = require('fs');
const path = require('path');

// 配置body-parser,支持表单和JSON格式传参
app.use(express.urlencoded({ extended: true }));
app.use(express.json());

// POST接口:接收folder参数,返回对应视频流
app.post('/api/video', (req, res) => {
  try {
    const { folder } = req.body;
    if (!folder) {
      return res.status(400).send('请提供视频文件夹名称');
    }
    // 假设每个文件夹下有默认视频文件video.mp4,可根据实际调整文件名规则
    const videoPath = path.join('D:/VideoDirectory', folder, 'video.mp4');
    
    // 检查文件是否存在
    if (!fs.existsSync(videoPath)) {
      return res.status(404).send('视频文件不存在');
    }

    // 获取文件信息,处理浏览器的分段播放请求
    const stat = fs.statSync(videoPath);
    const fileSize = stat.size;
    const range = req.headers.range;

    if (range) {
      const parts = range.replace(/bytes=/, "").split("-");
      const start = parseInt(parts[0], 10);
      const end = parts[1] ? parseInt(parts[1], 10) : fileSize - 1;
      const chunksize = (end - start) + 1;
      const file = fs.createReadStream(videoPath, { start, end });
      const head = {
        'Content-Range': `bytes ${start}-${end}/${fileSize}`,
        'Accept-Ranges': 'bytes',
        'Content-Length': chunksize,
        'Content-Type': 'video/mp4',
      };
      res.writeHead(206, head);
      file.pipe(res);
    } else {
      const head = {
        'Content-Length': fileSize,
        'Content-Type': 'video/mp4',
      };
      res.writeHead(200, head);
      fs.createReadStream(videoPath).pipe(res);
    }
  } catch (err) {
    console.error('视频请求错误:', err);
    res.status(500).send('服务器内部错误');
  }
});

// 启动服务器
const PORT = 3000;
app.listen(PORT, () => {
  console.log(`服务器运行在 http://localhost:${PORT}`);
});

前端无刷新切换视频的实现

这里给你两种实用方案,根据视频大小选择即可:

方案1:小视频首选——POST获取Blob流直接切换

通过POST请求拿到视频Blob,转成临时URL给video标签,实现无刷新切换:

<!DOCTYPE html>
<html>
<head>
  <title>无刷新切换视频</title>
</head>
<body>
  <video id="videoPlayer" controls width="600"></video>
  <button onclick="switchVideo('video1')">播放视频1</button>
  <button onclick="switchVideo('video2')">播放视频2</button>

  <script>
    async function switchVideo(folderName) {
      const videoPlayer = document.getElementById('videoPlayer');
      try {
        const response = await fetch('/api/video', {
          method: 'POST',
          headers: {
            'Content-Type': 'application/json',
          },
          body: JSON.stringify({ folder: folderName })
        });

        if (!response.ok) {
          throw new Error(await response.text());
        }

        // 将响应转成Blob,生成临时URL
        const blob = await response.blob();
        const blobUrl = URL.createObjectURL(blob);
        
        // 切换视频源并播放
        videoPlayer.src = blobUrl;
        videoPlayer.load();
        videoPlayer.play();

        // 视频结束后释放临时URL,避免内存泄漏
        videoPlayer.onended = () => {
          URL.revokeObjectURL(blobUrl);
        };
      } catch (err) {
        alert('切换视频失败: ' + err.message);
      }
    }
  </script>
</body>
</html>

方案2:大视频首选——POST获取临时标识,再用GET拉流

大视频用Blob会占用过多内存,这种情况下可以先通过POST获取临时访问标识,再用GET请求流式加载视频:

后端新增接口

// 临时存储视频路径映射(生产环境建议用Redis/数据库)
const tempVideoMap = new Map();

// POST接口:生成视频临时访问ID
app.post('/api/switch-video', (req, res) => {
  const { folder } = req.body;
  if (!folder) {
    return res.status(400).send('请提供视频文件夹名称');
  }
  const videoPath = path.join('D:/VideoDirectory', folder, 'video.mp4');
  if (!fs.existsSync(videoPath)) {
    return res.status(404).send('视频文件不存在');
  }
  // 生成唯一临时ID,5分钟后自动过期
  const tempId = Date.now().toString();
  tempVideoMap.set(tempId, videoPath);
  setTimeout(() => tempVideoMap.delete(tempId), 5 * 60 * 1000);
  res.json({ tempId });
});

// GET接口:根据临时ID返回视频流
app.get('/api/video-stream/:tempId', (req, res) => {
  const { tempId } = req.params;
  const videoPath = tempVideoMap.get(tempId);
  if (!videoPath) {
    return res.status(404).send('视频已过期或不存在');
  }
  // 复制之前POST接口里的流处理逻辑
  const stat = fs.statSync(videoPath);
  const fileSize = stat.size;
  const range = req.headers.range;
  if (range) {
    const parts = range.replace(/bytes=/, "").split("-");
    const start = parseInt(parts[0], 10);
    const end = parts[1] ? parseInt(parts[1], 10) : fileSize - 1;
    const chunksize = (end - start) + 1;
    const file = fs.createReadStream(videoPath, { start, end });
    const head = {
      'Content-Range': `bytes ${start}-${end}/${fileSize}`,
      'Accept-Ranges': 'bytes',
      'Content-Length': chunksize,
      'Content-Type': 'video/mp4',
    };
    res.writeHead(206, head);
    file.pipe(res);
  } else {
    const head = {
      'Content-Length': fileSize,
      'Content-Type': 'video/mp4',
    };
    res.writeHead(200, head);
    fs.createReadStream(videoPath).pipe(res);
  }
});

前端代码

<!DOCTYPE html>
<html>
<head>
  <title>无刷新切换视频</title>
</head>
<body>
  <video id="videoPlayer" controls width="600"></video>
  <button onclick="switchVideo('video1')">播放视频1</button>
  <button onclick="switchVideo('video2')">播放视频2</button>

  <script>
    async function switchVideo(folderName) {
      const videoPlayer = document.getElementById('videoPlayer');
      try {
        // 第一步:POST请求获取临时ID
        const switchResponse = await fetch('/api/switch-video', {
          method: 'POST',
          headers: {
            'Content-Type': 'application/json',
          },
          body: JSON.stringify({ folder: folderName })
        });

        if (!switchResponse.ok) {
          throw new Error(await switchResponse.text());
        }

        const { tempId } = await switchResponse.json();

        // 第二步:用临时ID加载视频流
        videoPlayer.src = `/api/video-stream/${tempId}`;
        videoPlayer.load();
        videoPlayer.play();
      } catch (err) {
        alert('切换视频失败: ' + err.message);
      }
    }
  </script>
</body>
</html>

注意事项

  • 确保D:/VideoDirectory下的子文件夹(如video1、video2)里存在对应视频文件,可根据实际文件名修改代码中的文件名规则。
  • 生产环境需添加权限控制,避免未授权用户访问服务器视频文件。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.20 11:31:04