1、客户端
准备工作,需要使用到的包有
rrweb(录制)
rrwebPlayer(播放)
pako(压缩)
1.1、录制:1.2、pako 压缩工具的使用方式
import * as rrweb from 'rrweb'let dispose = null
let rrwebEvents = []console.log('开始录制 ========>>>>>>>>>>>>>>')
clearFile(); // 录制之前清楚本地缓存json文件
dispose = rrweb.record({emit(event) {rrwebEvents.push(event);// 以 rrwebEvents 的长度作为分片持续上传:if(rrwebEvents.length >= 50) {uploadFile();// 分片之后清空队列: rrwebEvents = [];}}
})console.log('结束录制 ========>>>>>>>>>>>>>>')
if (dispose) {// 实际场景需要分片传输的话,就不需要这个关闭,当真正业务不需要录制的时候再调用// 分片逻辑是,序列化的rrwebEvents.length的累加大于某个值 --- 分片逻辑看业务需求吧// 我们这里上传的时候是 gzip 后的文件上传,不是用的json方式,也需要后端解gzip,这里只负责演示前端,所以压缩逻辑就没写上// 压缩逻辑是 引入pako, 然后调用,就不体现了哈dispose()
}
if (rrwebEvents.length === 0) return
uploadFile();
rrwebEvents = []
1.2、pako 压缩工具的使用方式
import pako from 'pako'// 压缩
const data = "Hello, world!"; // 待压缩的数据
const compressedData = pako.deflate(data); // 使用deflate方法进行压缩
console.log(compressedData); // 输出压缩后的数据 Uint8Array(21) [120, 156, 243, 72, 205, 201, 201, 215, 81, 40, 207, 47, 202, 73, 81, 4, 0, 32, 94, 4, 138]// 解压
const decompressedData = pako.inflate(compressedData); // 使用inflate方法进行解压缩
const unData = new TextDecoder().decode(decompressedData);
console.log(unData, '解压'); // 输出解压缩后的数据 Hello, world! 解压
1.3、调用接口
// 接口上传文件
function uploadFile() {console.log('上传文件 ========>>>>>>>>>>>>>>', '\n', rrwebEvents);fetch('http://localhost:8076/upload', {method: 'POST',headers: {'Content-type': 'application/json'},body: JSON.stringify({rrwebEvents})}).then(response => {console.log('response', response)}).catch(error => {console.log('error', error)})
}// 清除文件
function clearFile() {fetch('http://localhost:8076/clearFile', {method: 'POST'}).then(response => {console.log('response', response)}).catch(error => {console.log('error', error)})
}function getFile() {fetch('http://localhost:8076/getFile', {method: 'POST'}).then(response => response.json()).then(data => {new rrwebPlayer({target: document.getElementById('rrwebplayer'),data: {events: data.data,skipInactive: true,showDebug: true,showWarning: true,autoPlay: true}})})
}
2、服务端
使用node的Koa进行开发简易的服务端,使用文件存储的形式
const Koa = require('koa')
const router = require('koa-router')()
const cors = require('koa-cors')
const bodyParser = require('koa-bodyparser')
const fs = require('fs')
const path = require('path')
const app = new Koa()
app.use(cors())
app.use(bodyParser())app.use(router.routes())
app.use(async (ctx, next) => {ctx.set('Access-Control-Allow-Origin', '*')ctx.set('Access-Control-Allow-Headers','Content-Type, Content-Length, Authorization, Accept, X-Requested-With , yourHeaderFeild')ctx.set('Access-Control-Allow-Methods', 'PUT, POST, GET, DELETE, OPTIONS')if (ctx.method == 'OPTIONS') {ctx.body = 200} else {await next()}
})router.post('/upload', ctx => {const jsonFile = path.join(process.cwd(), `./file/jsonFile${Date.now()}.json`)fs.writeFileSync(jsonFile, JSON.stringify(ctx.request.body.rrwebEvents))ctx.response.body = {status: '00'}
})router.post('/getFile', ctx => {const fileDirPath = path.join(process.cwd(), `./file`);const files = fs.readdirSync(fileDirPath);let file;if(files && files.length) {file = fs.readFileSync(`${fileDirPath}/${files[0]}`);}ctx.response.body = {status: '00',data: JSON.parse(file)}
})router.post('/clearFile', ctx => {const fileDirPath = path.join(process.cwd(), `./file`);const files = fs.readdirSync(fileDirPath);if(files && files.length) {files.forEach(item => {const filePath = `${fileDirPath}/${item}`;fs.unlinkSync(filePath);})}ctx.response.body = {status: '00'}
})app.listen(8076)
console.log('listen on 8076')