图片、视频加载方式
This commit is contained in:
@@ -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
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user