feat: 监控上传图片压缩

This commit is contained in:
shawko 2026-05-12 11:24:05 +08:00
parent dd9000bf48
commit 23c87c43e1

View File

@ -15,6 +15,9 @@ const MONITOR_SNAPSHOT_INTERVAL = 10000
const MONITOR_MSG_TYPE = 'monitorStart' const MONITOR_MSG_TYPE = 'monitorStart'
const MONITOR_STOP_MSG_TYPE = 'monitorStop' const MONITOR_STOP_MSG_TYPE = 'monitorStop'
const MONITOR_LOG_PREFIX = '[class-monitor]' const MONITOR_LOG_PREFIX = '[class-monitor]'
const MONITOR_SNAPSHOT_TARGET_SIZE = 100 * 1024
const MONITOR_SNAPSHOT_MIN_WIDTH = 480
const MONITOR_SNAPSHOT_MIN_HEIGHT = 270
const monitorLog = (...args: unknown[]) => console.log(MONITOR_LOG_PREFIX, ...args) const monitorLog = (...args: unknown[]) => console.log(MONITOR_LOG_PREFIX, ...args)
const monitorWarn = (...args: unknown[]) => console.warn(MONITOR_LOG_PREFIX, ...args) const monitorWarn = (...args: unknown[]) => console.warn(MONITOR_LOG_PREFIX, ...args)
@ -119,20 +122,84 @@ const prepareMonitorDomForCapture = async () => {
}) })
} }
const canvasToPngFile = (canvas: HTMLCanvasElement) => const canvasToBlob = (canvas: HTMLCanvasElement, type: string, quality?: number) =>
new Promise<File | null>(resolve => { new Promise<Blob | null>(resolve => {
canvas.toBlob(blob => { canvas.toBlob(
if (!blob) return resolve(null) blob => {
resolve(new File([blob], `monitor-${Date.now()}.png`, { type: 'image/png' })) resolve(blob)
}, 'image/png') },
type,
quality,
)
}) })
const resizeCanvas = (source: HTMLCanvasElement, ratio: number) => {
const canvas = document.createElement('canvas')
canvas.width = Math.max(1, Math.round(source.width * ratio))
canvas.height = Math.max(1, Math.round(source.height * ratio))
const ctx = canvas.getContext('2d')
if (!ctx) return source
ctx.drawImage(source, 0, 0, canvas.width, canvas.height)
return canvas
}
const canvasToMonitorFile = async (canvas: HTMLCanvasElement) => {
const qualities = [0.72, 0.6, 0.5, 0.42, 0.35, 0.3, 0.25]
let current = canvas
let bestBlob: Blob | null = null
let bestInfo = {
width: canvas.width,
height: canvas.height,
quality: qualities[0],
}
for (let resizeCount = 0; resizeCount < 8; resizeCount++) {
for (const quality of qualities) {
const blob = await canvasToBlob(current, 'image/jpeg', quality)
if (!blob) return null
if (!bestBlob || blob.size < bestBlob.size) {
bestBlob = blob
bestInfo = {
width: current.width,
height: current.height,
quality,
}
}
if (blob.size <= MONITOR_SNAPSHOT_TARGET_SIZE) {
monitorLog('web screenshot compressed', {
width: current.width,
height: current.height,
quality,
size: blob.size,
})
return new File([blob], `monitor-${Date.now()}.jpg`, { type: 'image/jpeg' })
}
}
if (
current.width <= MONITOR_SNAPSHOT_MIN_WIDTH ||
current.height <= MONITOR_SNAPSHOT_MIN_HEIGHT
) {
break
}
current = resizeCanvas(current, 0.85)
}
if (!bestBlob) return null
monitorWarn('web screenshot compressed but still over target', {
...bestInfo,
size: bestBlob.size,
target: MONITOR_SNAPSHOT_TARGET_SIZE,
})
return new File([bestBlob], `monitor-${Date.now()}.jpg`, { type: 'image/jpeg' })
}
const createWebScreenshotFile = async (): Promise<File | null> => { const createWebScreenshotFile = async (): Promise<File | null> => {
await prepareMonitorDomForCapture() await prepareMonitorDomForCapture()
const vw = window.innerWidth const vw = window.innerWidth
const vh = window.innerHeight const vh = window.innerHeight
const scale = Math.min(window.devicePixelRatio || 1, 2) const scale = Math.min(window.devicePixelRatio || 1, 1)
monitorLog('web screenshot start', { monitorLog('web screenshot start', {
href: window.location.href, href: window.location.href,
viewport: `${vw}x${vh}`, viewport: `${vw}x${vh}`,
@ -200,7 +267,7 @@ const createWebScreenshotFile = async (): Promise<File | null> => {
monitorWarn('web screenshot canvas empty', index + 1) monitorWarn('web screenshot canvas empty', index + 1)
continue continue
} }
const file = await canvasToPngFile(canvas) const file = await canvasToMonitorFile(canvas)
if (file) { if (file) {
monitorLog('web screenshot file ready', { monitorLog('web screenshot file ready', {
attempt: index + 1, attempt: index + 1,