NodeJs 实现简单视频播放

# NodeJs 实现简单视频播放

# 实际展示效果

实际展示页

# 准备

目录结构

本次使用 nodeJs express 框架实现简单视频播放,需安装使用 nodeJs

  1. 新建一个文件夹 命名为 node-video,创建完后进入文件夹

  2. 初始化项目

    执行初始化命令,按照默认生成 package.json 文件
    npm init -y

  3. 安装express

    安装 express
    npm i express

  4. 按照上图创建相关目录 及 目录文件

    static 文件夹中放置视频播放 html 页面
    static/index.html 视频播放 html 页面
    video 文件夹中放置 视频文件
    index.js 为 nodeJs 入口文件

# 主要页面代码

# nodeJs 服务代码

index.js

var express = require('express')
var app = express()
var fs = require('fs')
var path = require('path')

// 设置静态资源文件目录
app.use(express.static(path.resolve(__dirname, './static')))

// 返回体
let obj = {
  code: '0',
  success: true,
  msg: 'ok',
  data: null,
}

// 获取播放列表
app.get('/videList', (req, res, next) => {
  // 获取 video 目录下的视频文件
  fs.readdir('./video', 'utf8', (err, file) => {
    if (err) {
      obj.code = '-1'
      obj.success = false
      obj.msg = err
      obj.data = null
    } else {
      obj.code = '0'
      obj.success = true
      obj.msg = 'ok'
      obj.data = file
    }
    res.send(obj)
  })
})

// 根据名称,返回视频文件数据
app.get('/video', function(req, res, next) {
  const { name } = req.query
  let path = './video/' + name
  let stat
  let flag = false
  try {
    stat = fs.statSync(path)
  } catch (error) {
    flag = true
  }
  if (flag) {
    res.send({
      code: '-1',
      msg: 'no video',
    })
    return false
  }

  let fileSize = stat.size
  let range = req.headers.range
  let type = name.split('.')[1]
  let videoType = 'video/' + type

  if (range) {
    //有range头才使用206状态码
    let parts = range.replace(/bytes=/, '').split('-')
    let start = parseInt(parts[0], 10)
    let end = parts[1] ? parseInt(parts[1], 10) : start + 999999
    // end 在最后取值为 fileSize - 1
    end = end > fileSize - 1 ? fileSize - 1 : end
    let chunksize = end - start + 1
    let file = fs.createReadStream(path, { start, end })
    let head = {
      'Content-Range': `bytes ${start}-${end}/${fileSize}`,
      'Accept-Ranges': 'bytes',
      'Content-Length': chunksize,
      'Content-Type': videoType,
    }
    res.writeHead(206, head)
    file.pipe(res)
  } else {
    let head = {
      'Content-Length': fileSize,
      'Content-Type': videoType,
    }
    res.writeHead(200, head)
    fs.createReadStream(path).pipe(res)
  }
})

// 启动express服务器
var server = app.listen(8081, function() {
  var host = server.address().address
  var port = server.address().port
  console.log('应用实例,访问地址为 http://%s:%s', host, port)
})

# 视频播放页

static/index.html

<!DOCTYPE html>
<html lang="en">
  <head>
    <title>video</title>
    <!-- Video.js -->
    <link href="https://unpkg.com/video.js/dist/video-js.css" rel="stylesheet" />
    <script src="https://unpkg.com/video.js/dist/video.min.js"></script>
    <script src="https://unpkg.com/flv.js/dist/flv.min.js"></script>
    <script src="https://unpkg.com/videojs-flvjs/dist/videojs-flvjs.min.js"></script>
    <script src="https://unpkg.com/axios/dist/axios.min.js"></script>
    <!-- 简单样式美化 -->
    <style>
      * {
        margin: 0;
        padding: 0;
      }
      html,
      body {
        width: 100%;
        height: 100%;
        position: relative;
        background-color: rgba(0, 0, 0, 0.5);
      }

      .content {
        width: 100%;
        min-width: 1000px;
        height: 100%;
        position: relative;
        display: flex;
        justify-content: center;
        align-items: center;
        /* 背景图 可替换为任意一张 1920*1080 或使用背景色 */
        background: url('./timg.jpg') no-repeat center center / cover;
        /* background-color: rgba(0, 0, 0, 0.9); */
      }
      .contentBox {
        display: flex;
        justify-content: center;
        align-items: center;
        padding: 15px;
        border: 1px solid #ccc;
        border-radius: 15px;
        background-color: rgba(255, 255, 255, 0.7);
      }
      #videoBox {
        width: 600px;
        height: 450px;
        border-radius: 15px;
        overflow: hidden;
      }
      #videoList {
        width: 250px;
        height: 450px;
        list-style: none;
        margin-left: 15px;
        padding-left: 15px;
        overflow-y: auto;
        background-color: rgba(255, 255, 255, 0.9);
        border-radius: 15px;
      }
      #videoList .Oli {
        height: 30px;
        line-height: 30px;
        margin-bottom: 6px;
        cursor: pointer;
      }
      #videoList .Oli:hover,
      #videoList .Oli.cur {
        color: red;
      }
    </style>
  </head>

  <body>
    <div class="content">
      <div class="contentBox">
        <div id="videoBox">
          <video id="videojs-flvjs-player" class="video-js vjs-default-skin vjs-big-play-centered"></video>
        </div>
        <ul id="videoList"></ul>
      </div>
    </div>

    <script>
      var videoName = '2.flv'
      var videoType = 'video/flv'

      function myVideo(Oid) {
        // 调用video插件 播放视频
        return videojs(
          Oid,
          {
            techOrder: ['html5', 'flvjs'],
            flvjs: {
              mediaDataSource: {
                isLive: false,
                cors: true,
                withCredentials: false,
              },
            },
            playbackRates: [1, 1.5, 2, 3], // 配置播放速率
            aspectRatio: '4:3',
            fluid: true,
            controls: true,
            preload: 'none',
          },
          function() {
            console.log('player ready')
            player.src({
              src: 'http://localhost:8081/video?name=' + videoName,
              type: videoType,
            })
            player.ready(function() {
              console.log('player ready')
              player.load()
              player.play()
            })
            player.on('error', (err) => {
              console.log('video load error', err)
              // player.errorDisplay.close()
              player.pause()
            })
          }
        )
      }
      var player = myVideo('videojs-flvjs-player')

      function done(name) {
        // 视频播放事件
        const type = name.split('.')[1]
        // 获取视频格式 此次只配置 flv 和 MP4 格式,其他格式可自行配置
        if (type === 'flv') {
          videoType = 'video/flv'
        } else {
          videoType = 'video/mp4'
        }
        videoName = name
        player = myVideo('videojs-flvjs-player')
      }

      function handleClick(name, index) {
        // 播放列表点击事件
        const Oul = document.getElementById('videoList')
        const Ali = Oul.getElementsByTagName('li')

        for (let i = 0; i < Ali.length; i++) {
          if (index == i) {
            // 设置播放列表当前播放项类名
            Ali[i].classList = 'Oli cur'
          } else {
            Ali[i].classList = 'Oli'
          }
        }
        done(name)
      }

      function createVideoList() {
        // 获取播放列表
        const Oul = document.getElementById('videoList')
        axios.get('http://localhost:8081/videList').then((res) => {
          if (res.data.data.length) {
            for (let i = 0; i < res.data.data.length; i++) {
              const Oli = document.createElement('li')
              Oli.classList = 'Oli'
              // 绑定播放列表项点击事件
              Oli.onclick = function() {
                handleClick(res.data.data[i], i)
              }
              Oli.innerHTML = res.data.data[i]
              Oul.append(Oli)
            }
          }
        })
      }
      // 初始化时生成播放列表
      createVideoList()
    </script>
  </body>
</html>

# package.json

{
  "name": "node-video",
  "version": "1.0.0",
  "description": "nodeJs实现简单视频播放功能,express + videoJs",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "xiaoc",
  "license": "ISC",
  "dependencies": {
    "express": "^4.17.1"
  }
}

# 说明

根据以上步骤配置好了后
进入项目目录
video目录中放置几个.mp4.flv格式的视频文件
执行 node index.js,启动nodeJs服务器
打开http://localhost:8081即可看到效果页