/*
* 视频上传组件
*/
<template>
<div>
<a-upload
listType="picture-card"
:accept="acceptType"
:action="uploadVideo"
:fileList="fileList"
:multiple="false"
:disabled="disabled"
@preview="handlePreview"
:beforeUpload="beforeUpload"
@change="event => handleChange(event, 'fileList')"
v-decorator="['fileList', {rules:[{required: requiresUpload, message: '请上传视频!'}]}]"
>
<div v-if="fileList.length < limit"> <a-icon type="plus"/> </div>
</a-upload>
<a-modal :visible="previewVisible" :footer="null" @cancel="handleVideoCancel">
<video alt="example" style="width: 100%" :src="videoUrl" controls>
当前浏览器不支持 video 功能
</video>
</a-modal>
</div>
</template>
<script>
import { FILE_DISPLAY_PREFIX, parseFileRespon, UPLOAD_URL, getVideoFrameImage } from '@/api/upload'
const uidGenerator = () => {
return '-' + parseInt(Math.random() * 10000 + 1, 10)
}
const getFileName = (path) => {
if (path.lastIndexOf('\\') >= 0) {
const reg = new RegExp('\\\\', 'g')
path = path.replace(reg, '/')
}
return path.substring(path.lastIndexOf('/') + 1)
}
export default {
name: 'UploadVideo',
props: {
// 是否前端进行展示缩略图(前端获取缩略图处理)
showThumbnail: {
type: Boolean,
default: false
},
// 上传文件数量限制, 默认1个
limit: {
type: Number,
default: 1
},
// 上传文件大小限制, 单位Mb, 默认无限制
fileSize: {
type: Number,
default: 0
},
// 是否必须上传验证
requiresUpload: {
type: Boolean,
default: false
},
acceptType: {
type: String,
required: false,
default: '.mp4, .3gp, .rmvb, .wmv, .flv, .avi'
},
text: {
type: String,
required: false,
default: '点击上传'
},
// 初始视频内容
value: {
type: [String, Array],
required: false,
default: ''
},
// 组件是否能够上传, 默认支持上传
disabled: {
type: Boolean,
default: false
}
},
data () {
return {
fileDisplayPrefix: FILE_DISPLAY_PREFIX,
parseFileRespon,
// 上传地址
uploadVideo: UPLOAD_URL + '/files',
// 视频预览地址
videoUrl: '',
headers: {},
fileList: [],
fileLoading: false,
previewVisible: false
}
},
watch: {
value (val) {
// console.log('初始化视频资源路径', val)
if (val instanceof Array) {
this.initFileList(val.join(','))
} else {
this.initFileList(val)
}
}
},
created () {
// const token = Vue.ls.get(ACCESS_TOKEN)
// this.headers = { 'X-Access-Token': token }
},
methods: {
initFileList (paths) {
if (!paths || paths.length === 0) {
this.fileList = []
return
}
const arr = paths.split(',')
if (this.showThumbnail) {
for (let a = 0; a < arr.length; a++) {
getVideoFrameImage(this.fileDisplayPrefix + arr[a]).then(res => {
this.fileList.push({
uid: uidGenerator(),
name: getFileName(arr[a]),
status: 'done',
url: res,
videoUrl: arr[a],
response: {
code: 10000,
status: 'history',
message: arr[a],
result: [ arr[a] ]
}
})
}).catch(e => {
this.fileList.push({
uid: uidGenerator(),
name: getFileName(arr[a]),
status: 'done',
url: e,
videoUrl: arr[a],
response: {
code: 10000,
status: 'history',
message: arr[a],
result: [ arr[a] ]
}
})
})
}
} else {
for (let a = 0; a < arr.length; a++) {
this.fileList.push({
uid: uidGenerator(),
name: getFileName(arr[a]),
status: 'done',
url: this.fileDisplayPrefix + arr[a],
videoUrl: arr[a],
response: {
code: 10000,
status: 'history',
message: arr[a],
result: [ arr[a] ]
}
})
}
}
// console.log('sss', fileList)
// this.fileList = fileList
},
handlePathChange () {
const uploadFiles = this.fileList
let path = ''
if (!uploadFiles || uploadFiles.length === 0) {
path = ''
}
const arr = []
for (var a = 0; a < uploadFiles.length; a++) {
if (uploadFiles[a].response) {
arr.push(uploadFiles[a].response.message)
}
}
if (arr.length > 0) {
path = arr.join(',')
}
this.$emit('change', path)
},
// 图片上传前的校验
beforeUpload (file) {
return true
},
handleChange (info, listStr) {
if (info.file.status) {
this[listStr] = info.fileList
}
if (info.file.status === 'uploading') {
this.fileLoading = true
} else if (info.file.status === 'error') {
this.$message.error(this.$t('服务器无响应,请联系管理员!'))
this.fileLoading = false
} else if (info.file.status === 'done') {
if (info.file.response.code === 10000) {
const that = this
// 缩略图处理
this[listStr][info.fileList.length - 1].url = this.fileDisplayPrefix + this.parseFileRespon(info.file.response) || ''
this[listStr][info.fileList.length - 1].videoUrl = this.parseFileRespon(info.file.response) || ''
// 前端缩略图处理
if (this.showThumbnail) {
getVideoFrameImage(that.fileList[that.fileList.length - 1].url).then(res => {
that.fileList[that.fileList.length - 1].url = res
that.$forceUpdate()
})
}
} else {
this.fileList.pop()
this.$message.error('上传视频失败!')
}
} else if (info.file.status === 'removed') {
this.$emit('change', this.fileList.map(i => i.videoUrl))
}
this.$forceUpdate()
},
handleVideoCancel () {
this.previewVisible = false
const myVideo = document.getElementsByTagName('video')
const arr = Array.from(myVideo)
// 关闭时暂停播放
arr.forEach(item => {
item.pause()
})
},
// 视频文件预览
async handlePreview (file) {
this.videoUrl = this.fileDisplayPrefix + file.videoUrl
// console.log('路径', this.videoUrl)
this.previewVisible = true
},
handleDelete (file) {
// 如有需要新增 删除逻辑
console.log(file)
},
// 获取上传的视频地址, 以,分割
getUrls () {
let urls = ''
if (this.fileList.length > 0) {
this.fileList.forEach(item => {
if (urls === '') {
urls = item.response ? this.parseFileRespon(item.response) : item.videoUrl.replaceAll('/uploads', '')
} else {
const urlPath = item.response ? this.parseFileRespon(item.response) : item.videoUrl.replaceAll('/uploads', '')
urls += ',' + urlPath
}
})
}
return urls
},
// 获取视频标题+时长+缩略图+宽高
asyncImgChecked (file) {
return new Promise((resolve, reject) => {
const reader = new FileReader()
// 必须用file.raw
reader.readAsDataURL(file)
// reader.readAsDataURL(file.raw)
reader.onload = () => {
// 让页面中的img标签的src指向读取的路径
const video = document.createElement('video')
video.src = reader.result
video.currentTime = 3 // 截取缩略图时的视频时长,一定要设置,不然大概率白屏
video.oncanplay = () => {
const canvas = document.createElement('canvas')
const ctx = canvas.getContext('2d')
canvas.setAttribute('width', 100)
canvas.setAttribute('height', 100)
video.setAttribute('width', 100)
video.setAttribute('height', 100)
ctx.drawImage(video, 0, 0, video.width, video.height)
const imgSrc = canvas.toDataURL('image/png')
resolve({
// base64的缩略图图片路径
thumbnail: imgSrc,
width: video.videoWidth,
height: video.videoHeight,
videoName: file.name
})
}
}
})
}
}
}
</script>
<style scoped>
</style>
附录upload.js内容
import path from './index'
import { isEmpty } from '@/utils/common'
import { axios } from '@/utils/request'
// 上传文件地址
export const UPLOAD_URL = process.env.VUE_APP_API_BASE_URL + path.upload
// 文件回显前缀
export const FILE_DISPLAY_PREFIX = path.uploads
// 解析文件响应
export function parseFileRespon (response) {
if (isEmpty(response) || response.code !== 10000 || response.result.length < 1) {
this.$message.error(() => {
return response.msg || '服务异常,请稍后再试'
})
return ''
} else {
return response.result[0]
}
}
/**
* 1分片上传 地址
* @returns
*/
export function chunk () {
return UPLOAD_URL + '/oss/aliyun/chunk'
}
/**
* 2分片上传完成
* @param identifier
* @returns {AxiosPromise}
*/
export function chunkComplete (identifier) {
return axios({
url: path.upload + '/oss/aliyun/chunk/complete',
method: 'post',
params: { 'identifier': identifier }
})
}
/**
* 3分片上传取消
* @param identifier
* @returns {AxiosPromise}
*/
export function chunkAbort (identifier) {
return axios({
url: path.upload + '/oss/aliyun/chunk/abort',
method: 'post',
params: { 'identifier': identifier }
})
}
/**
* 自定义上传图片
* @param file
* @returns {AxiosPromise}
*/
export function customUploadFile (file) {
const data = new FormData()
data.append('file', file)
return axios({
url: path.upload + '/files',
method: 'POST',
headers: { 'Content-Type': 'multipart/form-data' },
timeout: 1800000,
data: data
})
}
/**
* 获取缩略图
* @param url 地址
* @param currentTime 缩略图取第几秒的图片
* @param width
* @param height
* @returns {Promise<unknown>}
*/
export function getVideoFrameImage (url, currentTime = 3, width = 100, height = 100) {
return new Promise((resolve, reject) => {
const video = document.createElement('video')
video.setAttribute('crossOrigin', 'anonymous')
video.setAttribute('preload', 'metadata')
video.setAttribute('src', url)
// 视频开始可能是黑屏状态
video.currentTime = currentTime
video.addEventListener('loadeddata', function () {
const canvas = document.createElement('canvas')
const { videoWidth, videoHeight } = video
const newHeight = height || videoHeight * (width / videoWidth)
canvas.width = width
canvas.height = newHeight
canvas.getContext('2d').drawImage(video, 0, 0, 200, height)
resolve(canvas.toDataURL('image/jpeg'))
})
// 错误时的特殊处理,(资源404或不可用时触发)
video.addEventListener('error', function (e) {
// console.log('ss', e)
reject(url)
})
})
}
标签:arr,vue,const,fileList,ant,video,file,design,response From: https://www.cnblogs.com/qiushuiyu-108/p/17197330.html