当前位置:网站首页>富媒体在客服IM消息通信中的秒发实践
富媒体在客服IM消息通信中的秒发实践
2022-08-09 11:08:00 【InfoQ】
引言
一、背景
二、面临的挑战

三、解决方案与成效
export function getFileInfo(file: File): Promise<any> {
return new Promise((resolve, reject) => {
try {
const reader = new FileReader()
reader.readAsDataURL(file)
reader.onload = (event: ProgressEvent<FileReader>) => {
resolve(event)
}
} catch (e) {
reject(e)
}
})
}
export function getVideoInfo(file) {
return new Promise((resolve, reject) => {
getFileInfo(file)
.then(fileReader => {
const target = fileReader.target.result
if (/video/g.test(file.type)) {
const video = document.createElement('video')
video.muted = true
video.setAttribute('autoplay', 'autoplay')
video.setAttribute('src', target)
video.addEventListener('loadeddata', () => {
// ...
})
video.onerror = e => reject(e)
}
})
.catch(e => reject(e))
})
}

if (/*******/) {
// ...
//. blob作为预览视频的url
state.previewVideoSrc = URL.createObjectURL(file)
state.previewVideo = true
state.cachePreviewVideoFile = file
nextTick(() => {
focus()
})
} else {
// ...
}

export function getVideoInfo(file, msgid?: string) {
return new Promise((resolve, reject) => {
getFileInfo(file, msgid)
.then(fileReader => {
const target = fileReader.target.result
if (/video/g.test(file.type)) {
const video = document.createElement('video')
video.muted = true
video.setAttribute('autoplay', 'autoplay')
// target只作为url创建视频用于获取视频大小、播放时长等基本信息,不用于页面渲染
video.setAttribute('src', target)
video.addEventListener('loadeddata', () => {
const canvas = document.createElement('canvas')
canvas.width = video.videoWidth
canvas.height = video.videoHeight
const width = video.videoWidth
const height = video.videoHeight
canvas.getContext('2d')!.drawImage(video, 0, 0, width, height)
const src = canvas.toDataURL('image/jpg')
const imgFile = dataURLtoFile(src, `视频_${Math.random()}.png`)
return getImgInfo(imgFile, fileReader.msgid).then(
({ width: imgWidth, height: imgHeight, file: imgFile, size: imgSize, src: imgSrc, msgid }) => {
resolve({
// ...
})
}
)
})
video.onerror = e => {
// ...
reject(e)
}
}
})
.catch(e => {
reject(e)
})
})
}
// 拼接的获取图片首帧的URL地址
export const thumbSuffix = `?x-oss-process=video/snapshot,****`
export function addOssImageParams(url, isThumb = false) {
const suffix = isThumb ? thumbSuffix : urlSuffix
if (!url) return ''
// ...
return url
}

- 浏览器主线程在执行发送文件的时候,如果发送文件任务没有结束,则会阻塞其他的任务,相当于发送期间,客服什么事情也做不了;
- 浏览器主子线程在执行发送文件的时候,通过子线程读取文件,在读取文件期间,主线程可以继续执行其他的任务,等到子线程读取完文件通过postMessage发送相关的信息告知主线程文件读取完毕,主线程再开始渲染。整个过程对于客服没有任何阻塞。

// 子线程任务
export function subWork() {
self.onmessage = ({ data: { file } }) => {
try {
// 读取文件信息
// ...
// 发送对应信息
self.postMessage({ fileReader: **** })
} catch (e) {
self.postMessage({ fileReader: undefined })
}
}
}
export const createWorker = (subWorker, file, resolve, reject) => {
const worker = new Worker(URL.createObjectURL(new Blob([`(${subWorker.toString()})()`])))
// 发到子线程
worker.postMessage({
file
})
// 监听子线程返回数据
worker.onmessage = ({ data: { fileReader } }) => {
resolve(fileReader)
// 获取到结果后关闭线程
worker.terminate()
}
// 监听异常
worker.onmessageerror = function () {
worker.terminate()
}
}
// 创建主线程任务
export const getFileInfoFromSubWorker = files => {
return new Promise((resolve, reject) => {
createWorker(subWork, files, resolve, reject)
})
}
export function getVideoInfo(file, blob, msgid?: string) {
return new Promise((resolve, reject) => {
if (/video/g.test(file.type)) {
const video = document.createElement('video')
video.muted = true
video.setAttribute('autoplay', 'autoplay')
// blob作为url: URL.createObjectURL(file)
video.setAttribute('src', blob)
video.addEventListener('loadeddata', () => {
const width = video.videoWidth
const height = video.videoHeight
resolve({
videoWidth: width,
videoHeight: height,
videoDuration: video.duration * 1000,
videoFile: file,
videoSize: file.size,
videoSrc: blob,
msgid
})
})
video.onerror = e => {
reject(e)
}
}
})
}
四、总结
五、知识扩展
1、文件读取的实现差异
- 通过FileReader.readAsDataURL(file)获取到的是一段data:base64的字符串,base64位的字符串较大
- 通过URL.createObjectURL(blob)获会创建一个DOMString,其中有包含了文件信息的URL(指定的 File 对象或 Blob 对象)
- createObjectURL是立即的执行
- FileReader.readAsDataURL是(过一段时间)异步执行
- createObjectURL返回一段带hash的url,并且一直存储在内存中,当document被触发了unload或者执行revokeObjectURL进行内存释放;
- FileReader.readAsDataURL返回的是base64的字符串,比blob url消耗更多的内存,不过这个数据会通过垃圾回收机制自动清除。
- 用createObjectURL能够节省性能,获取的速度也更快;
- 如果设备性能足够好,而且想要获取图片的base64,可以用FileReader.readAsDataURL。
2、流媒体、富媒体、多媒体的概念
边栏推荐
猜你喜欢
电磁场与电磁波-场论基础
linux mysql操作的相关命令
sublime记录
【VIBE: Video Inference for Human Body Pose and Shape Estimation】论文阅读
FreeRTOS任务创建源码分析
七夕?程序员不存在的~
备战金三银四:如何成功拿到阿里offer(经历+面试题+如何准备)
支付宝小程序的接入
信号量SIGCHLD的使用,如何让父进程得知子进程执行结束,如何让父进程区分多个子进程的结束
Preparation for gold three silver four: how to successfully get an Ali offer (experience + interview questions + how to prepare)
随机推荐
基于STM32F103移植FreeRTOS
People | How did I grow quickly from programmer to architect?
OpenSSF的开源软件风险评估工具:Scorecards
MDK添加注释模板
PTA 找出不是两个数组共有的元素
focusablejs
FreeRTOS列表和列表项源码分析
golang源代码阅读,sync系列-Pool
For versions corresponding to tensorflow and numpy, report FutureWarning: Passing (type, 1) or '1type' as a synonym of type is deprecate
Solve 1. tensorflow runs using CPU but not GPU 2. GPU version number in tensorflow environment 3. Correspondence between tensorflow and cuda and cudnn versions 4. Check cuda and cudnn versions
C语言统计不同单词数
ACM最长不下降子序列问题
微信小程序——天气查询
x86异常处理与中断机制(2)中断向量表
PTA 换硬币
详细的np.matmul / np.dot / np.multiply / tf.matmul / tf.multiply / *
1003 Emergency (25分)
信息系统项目的十大管理
ACM01背包问题
String类型的字符串对象转实体类和String类型的Array转List