/** * @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