大文件分片上传

140 阅读1分钟

一、效果

动画.gif

二、实现分片上传

2.1 开启node服务

  1. 创建server文件夹,执行npm init
  2. 安装依赖:yarn add express express-fileupload nodemon
  3. 创建执行命令:"dev": "nodemon ./app.js"
  4. 创建app.js(提供一个上传接口)和files(存储客户端上传的文件)

app.js

const express = require('express')
const uploader = require('express-fileupload')
const bodyParser = require('body-parser')
const { extname, resolve } = require('path')
const { existsSync, appendFileSync, writeFileSync } = require('fs')

const app = express()
const PORT = 8008

app.use(bodyParser.urlencoded({ extended: true }))
app.use(bodyParser.json())
app.use(uploader())
app.use('/', express.static('files'))

const ALLOWED_TYPE = { 'video/mp4': 'mp4', 'video/ogg': 'ogg' }

// 跨域
app.all('*', (req, res, next) => {
  res.header('Access-Control-Allow-origin', '*')
  res.header('Access-Control-Allow-Methods', 'POST,GET')
  next()
})

app.post('/upload_video', (req, res) => {
  const { name, type, size, fileName, uploadSize } = req.body
  const { file } = req.files
  if (!file) {
    req.send({ code: 1001, msg: '未上传文件' })
    return
  }
  if (!ALLOWED_TYPE[type]) {
    res.send({ code: 1002, msg: '类型不符合规定' })
    return
  }
  const filename = fileName + extname(name)
  const filePath = resolve(__dirname, `./files/${filename}`)
  if (uploadSize !== '0') {
    if (!existsSync(filePath)) {
      res.send({ code: 1003, msg: '文件不存在' })
      return
    }
    appendFileSync(filePath, file.data)
    res.send({ code: 0, msg: '追加成功', video_url: `http://localhost:8008/${filename}` })
    return
  }
  writeFileSync(filePath, file.data)
  res.send({ code: 0, msg: '文件已创建', video_url: `http://localhost:8008/${filename}` })
})

app.listen(PORT, () => {
  console.log('服务已启动,端口号:', PORT)
})

终端执行npm run dev

image.png

2.2 创建BigFile组件

<template>
  <div class="">
    上传进度:<progress :value="progressValue" :max="pregressMax" />
    <input ref="inputRef" type="file" />
    <el-button @click="handleUpload">上传</el-button>
  </div>
</template>

<script>
const CHUNK_SIZE = 1024 * 1024
import axios from 'axios'
export default {
  name: 'BigFile',
  data() {
    return {
      progressValue: 0,
      pregressMax: 1,
      uploadSize: 0
    }
  },
  created() {},
  methods: {
    async handleUpload() {
      const [file] = this.$refs.inputRef.files
      if (!file) {
        return this.$message.warning('请先选择文件')
      }
      const { name, type, size } = file
      if (type !== 'video/mp4') {
        return this.$message.warning('文件格式不对')
      }
      const fileName = Date.now() + '_' + name
      let uploadResult = null
      this.pregressMax = size
      while (this.uploadSize < size) {
        const fileChunk = file.slice(this.uploadSize, this.uploadSize + CHUNK_SIZE)
        const formData = this.createFormData({ name, type, size, fileName, uploadSize: this.uploadSize, file: fileChunk })
        try {
          uploadResult = await axios.post('http://localhost:8008/upload_video', formData)
        } catch (e) {
          return this.$message.error('上传失败:' + e.message)
        }
        this.uploadSize += fileChunk.size
        this.progressValue = this.uploadSize
      }
      console.log('uploadResult', uploadResult)
      this.createVideo(uploadResult.data.video_url)
    },
    createFormData({ name, type, size, fileName, uploadSize, file }) {
      const fd = new FormData()
      fd.append('name', name)
      fd.append('type', type)
      fd.append('size', size)
      fd.append('fileName', fileName)
      fd.append('uploadSize', uploadSize)
      fd.append('file', file)
      return fd
    },
    createVideo(src) {
      const oVideo = document.createElement('video')
      oVideo.controls = true
      oVideo.width = '300'
      oVideo.src = src
      document.querySelector('#app').appendChild(oVideo)
    }
  }
}
</script>

三、短点续传