/**
* @description 上传视频
* @author darin_l
*/
import Editor from '../../editor/index'
import { arrForEach, forEach } from '../../utils/util'
import post from '../../editor/upload/upload-core'
import Progress from '../../editor/upload/progress'
type ResType = {
errno: number | string
data: string[]
}
class UploadVideo {
private editor: Editor
constructor(editor: Editor) {
this.editor = editor
}
/**
* 提示信息
* @param alertInfo alert info
* @param debugInfo debug info
*/
private alert(alertInfo: string, debugInfo?: string): void {
const customAlert = this.editor.config.customAlert
if (customAlert) {
customAlert(alertInfo)
} else {
window.alert(alertInfo)
}
if (debugInfo) {
console.error('wangEditor: ' + debugInfo)
}
}
/**
* 往编辑区域插入视频
* @param src 视频地址
*/
public insertVideo(src: string): void {
const editor = this.editor
const config = editor.config
const i18nPrefix = 'validate.'
const t = (text: string, prefix: string = i18nPrefix): string => {
return editor.i18next.t(prefix + text)
}
// 先插入视频,无论是否能成功
editor.cmd.do('insertHTML', `
`)
// 执行回调函数
config.linkVideoCallback(src)
// 加载视频
let video: any = document.createElement('video')
video.onload = () => {
console.log("load complete");
video = null
}
video.onerror = (e: any) => {
this.alert(
t('插入视频错误'),
`wangEditor: ${t('插入视频错误')},${t('视频链接')} "${src}",${t('下载链接失败')}`
)
video = null
}
video.onabort = () => (video = null)
video.src = src
}
/**
* 上传视频
* @param files 文件列表
*/
public uploadVideo(files: FileList | File[]): void {
if (!files.length) {
return
}
const editor = this.editor
const config = editor.config
// ------------------------------ i18next ------------------------------
const i18nPrefix = 'validate.'
const t = (text: string): string => {
return editor.i18next.t(i18nPrefix + text)
}
// ------------------------------ 获取配置信息 ------------------------------
// 服务端地址
let uploadVideoServer = config.uploadVideoServer
// 视频最大体积
const maxSize = config.uploadVideoMaxSize
const maxSizeM = maxSize / 1024 / 1024
// 一次最多上传视频数量
const maxLength = config.uploadVideoMaxLength
// 自定义 fileName
const uploadVideoName = config.uploadVideoName
// 自定义参数
const uploadVideoParams = config.uploadVideoParams
// 参数拼接到 url 中
const uploadVideoParamsWithUrl = config.uploadVideoParamsWithUrl
// 自定义 header
const uploadVideoHeaders = config.uploadVideoHeaders
// 钩子函数
const hooks = config.uploadVideoHooks
// 上传视频超时时间
const timeout = config.uploadVideoTimeout
// 跨域带 cookie
const withCredentials = config.uploadVideoWithCredentials
// 自定义上传视频
const customUploadVideo = config.customUploadVideo
if (!customUploadVideo) {
// 没有 customUploadVideo 的情况下,需要如下配置才能继续进行视频上传
if (!uploadVideoServer) {
return
}
}
// ------------------------------ 验证文件信息 ------------------------------
const resultFiles: File[] = []
const errInfos: string[] = []
arrForEach(files, (file: File) => {
const name = file.name
const size = file.size
// chrome 低版本 name === undefined
if (!name || !size) {
return
}
if (/\.(mp4|rmvb|wma|avi|mov)$/i.test(name) === false) {
// 后缀名不合法,不是视频
errInfos.push(`【${name}】${t('不是视频')}`)
return
}
if (maxSize < size) {
// 上传视频过大
errInfos.push(`【${name}】${t('大于')} ${maxSizeM}M`)
return
}
// 验证通过的加入结果列表
resultFiles.push(file)
})
// 抛出验证信息
if (errInfos.length) {
this.alert(`${t('视频验证未通过')}: \n` + errInfos.join('\n'))
return
}
if (resultFiles.length > maxLength) {
this.alert(t('一次最多上传') + maxLength + t('张视频'))
return
}
// ------------------------------ 自定义上传 ------------------------------
if (customUploadVideo && typeof customUploadVideo === 'function') {
customUploadVideo(resultFiles, this.insertVideo.bind(this))
// 阻止以下代码执行,重要!!!
return
}
// ------------------------------ 上传视频 ------------------------------
// 添加视频数据
const formData = new FormData()
resultFiles.forEach((file: File, index: number) => {
let name = uploadVideoName || file.name
if (resultFiles.length > 1) {
// 多个文件时,filename 不能重复
name = name + (index + 1)
}
formData.append(name, file)
})
if (uploadVideoServer) {
// 添加自定义参数
const uploadVideoServerArr = uploadVideoServer.split('#')
uploadVideoServer = uploadVideoServerArr[0]
const uploadVideoServerHash = uploadVideoServerArr[1] || ''
forEach(uploadVideoParams, (key: string, val: string) => {
// 因使用者反应,自定义参数不能默认 encode ,由 v3.1.1 版本开始注释掉
// val = encodeURIComponent(val)
// 第一,将参数拼接到 url 中
if (uploadVideoParamsWithUrl) {
if (uploadVideoServer.indexOf('?') > 0) {
uploadVideoServer += '&'
} else {
uploadVideoServer += '?'
}
uploadVideoServer = uploadVideoServer + key + '=' + val
}
// 第二,将参数添加到 formData 中
formData.append(key, val)
})
if (uploadVideoServerHash) {
uploadVideoServer += '#' + uploadVideoServerHash
}
// 开始上传
const xhr = post(uploadVideoServer, {
timeout,
formData,
headers: uploadVideoHeaders,
withCredentials: !!withCredentials,
beforeSend: (xhr: XMLHttpRequest) => {
if (hooks.before) return hooks.before(xhr, editor, resultFiles)
},
onTimeout: (xhr: XMLHttpRequest) => {
this.alert(t('上传视频超时'))
if (hooks.timeout) hooks.timeout(xhr, editor)
},
onProgress: (percent: number, e: ProgressEvent) => {
const progressBar = new Progress(editor)
if (e.lengthComputable) {
percent = e.loaded / e.total
progressBar.show(percent)
}
},
onError: (xhr: XMLHttpRequest) => {
this.alert(
t('上传视频错误'),
`${t('上传视频错误')},${t('服务器返回状态')}: ${xhr.status}`
)
if (hooks.error) hooks.error(xhr, editor)
},
onFail: (xhr: XMLHttpRequest, resultStr: string) => {
this.alert(
t('上传视频失败'),
t('上传视频返回结果错误') + `,${t('返回结果')}: ` + resultStr
)
if (hooks.fail) hooks.fail(xhr, editor, resultStr)
},
onSuccess: (xhr: XMLHttpRequest, result: ResType) => {
if (hooks.customInsert) {
// 自定义插入视频
hooks.customInsert(this.insertVideo.bind(this), result, editor)
return
}
if (result.errno != '0') {
// 返回格式不对,应该为 { errno: 0, data: [...] }
this.alert(
t('上传视频失败'),
`${t('上传视频返回结果错误')},${t('返回结果')} errno=${result.errno}`
)
if (hooks.fail) hooks.fail(xhr, editor, result)
return
}
// 成功,插入视频
const data = result.data
data.forEach(link => {
this.insertVideo(link)
})
// 钩子函数
if (hooks.success) hooks.success(xhr, editor, result)
},
})
if (typeof xhr === 'string') {
// 上传被阻止
this.alert(xhr)
}
return
}
}
}
export default UploadVideo