图片、视频加载方式

This commit is contained in:
zhujingzhou
2025-07-20 11:36:46 +07:00
parent 2ab6513b73
commit fa06b597e5
17 changed files with 1204 additions and 375 deletions
+102
View File
@@ -0,0 +1,102 @@
// import { Buffer } from 'buffer/'
// 判断是否加解密,如果字节前几位对应上,则不需要解密
const encryptMagicNumber = new Uint8Array([
0x88,
0xA8,
0x30,
0xCB,
0x10,
0x76
])
// 解密密钥
// const _decryptKey = Buffer.from('2019ysapp7527') // 加密key
// 避免vite单独引入buffer库 提前进行编码
const _decryptKey = [50, 48, 49, 57, 121, 115, 97, 112, 112, 55, 53, 50, 55]// 加密key
const _encryptedLen = 100 // 加密图片的数据长度
// / 加密密钥(????,不解就去找游总解答)
const ENCRYPT_KEY = 0xA3
// 三种图片 type类型,根据前几子节可以判断出类型,这是固定的。如需要增加其他类型则自己上网找固定对应的字节。
const _featuresList = [
new Uint8Array([0xFF, 0xD8, 0xFF]), // jpg,jpeg
new Uint8Array([0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A]), // png
new Uint8Array([0x47, 0x49, 0x46]) // gif
]
/**
* 解密入口
*/
export function decryptImage(imgBytes) {
// 判断是否为空,如果传入的字节为空则不处理
if (imgBytes === null || imgBytes.length === 0) { return }
// 增加一个变量来判断是否存在
let isAll = false
// 循环魔法密钥,确定这是需要解密的图片是全部长度,而不是前100字节
for (let i = 0; i < encryptMagicNumber.length; i++) {
if (encryptMagicNumber[i] !== imgBytes[i]) { continue }
isAll = true
}
if (isAll) {
imgBytes = xorBaseAllLength(imgBytes)
} else {
// 是需要解密的
if (isEncryptedImage(imgBytes)) {
// 解密结果
imgBytes = xorBaseLength(imgBytes, _decryptKey, _encryptedLen)
}
}
return imgBytes
}
/**
* 返回一个判断值,true则代表 是png gif jpg三种图片中的一种,则需要解密
*/
function isEncryptedImage(imgBytes) {
let isDecrypted = false
const _featuresLen = _featuresList.length
for (let i = 0; i < _featuresLen; i++) {
// 每次进入则初始化变量的值为假
isDecrypted = false
for (let j = 0; j < _featuresList[i].length; j++) {
if (_featuresList[i][j] !== imgBytes[j]) {
isDecrypted = true
break
}
}
if (isDecrypted) { continue } else { break }
}
return isDecrypted
}
// 解密算法
function xorBaseLength(src, key, length) {
const srcLen = src.length
const keyLen = key.length
if (length > srcLen || length <= 0) { length = srcLen }
for (let i = 0; i < length; i += keyLen) {
for (let j = 0; j < keyLen && i + j < length; j++) { src[i + j] ^= key[j] }
}
return src
}
// 全部长度解密,解密算法
function xorBaseAllLength(src) {
let index = -1
// 这里map数组内 所有的字节做位异或运数,返回的是解密后的数组
const dest = src.map((it) => {
index++
if (
index < encryptMagicNumber.length &&
it === encryptMagicNumber[index]
) { return null }
return it ^ ENCRYPT_KEY
})
return dest
}
+86 -1
View File
@@ -1,7 +1,7 @@
/**
* Created by PanJiaChen on 16/11/18.
*/
import { decryptImage } from './imgCode.js'
/**
* Parse the time to string
* @param {(Object|string|number)} time
@@ -227,3 +227,88 @@ export function getQueryString(name) {
}
return null
}
export async function handleVerAutoImg(imageUrl) {
// 参数校验
if (!imageUrl || typeof imageUrl !== 'string') {
console.warn('无效的图片URL:', imageUrl)
return imageUrl // 返回原始URL
}
// 检查URL是否已经是Blob URL
if (imageUrl.startsWith('blob:')) {
return imageUrl
}
try {
const controller = new AbortController()
const timeoutId = setTimeout(() => controller.abort(), 10000) // 10秒超时
const response = await fetch(imageUrl, {
signal: controller.signal,
headers: {
'Accept': 'image/*'
}
}).finally(() => clearTimeout(timeoutId))
if (!response.ok) {
console.warn(`图片加载失败: ${response.status} ${response.statusText}`)
return imageUrl // 返回原始URL
}
const arrayBuffer = await response.arrayBuffer()
const bytes = new Uint8Array(arrayBuffer)
// 基本验证:检查是否为有效的图片数据
if (bytes.length === 0) {
// console.warn('图片数据为空')
return imageUrl
}
try {
// 尝试解密图片数据
const decryptedData = decryptImage(bytes)
// 验证解密后的数据
if (!decryptedData || decryptedData.length === 0) {
// console.warn('图片解密结果无效')
return imageUrl
}
// 创建Blob URL前检查数据有效性
const blob = new Blob([decryptedData], { type: 'image/jpeg' })
if (blob.size === 0) {
// console.warn('解密后的图片数据为空')
return imageUrl
}
const blobUrl = URL.createObjectURL(blob)
// 添加清理逻辑
window.addEventListener('unload', () => {
try {
URL.revokeObjectURL(blobUrl)
} catch (e) {
// 忽略清理错误
console.log(e, 'e')
}
})
return blobUrl
} catch (decryptError) {
console.warn('图片解密失败,使用原始URL:', decryptError)
return imageUrl
}
} catch (error) {
// 处理特定类型的错误
if (error.name === 'AbortError') {
// console.warn('图片加载超时')
} else if (error instanceof TypeError) {
// console.warn('网络请求失败')
} else {
// console.warn('图片处理失败:', error)
}
// 返回原始URL而不是抛出错误
return imageUrl
}
}