103 lines
3.0 KiB
TypeScript
103 lines
3.0 KiB
TypeScript
|
|
import type {Upload} from './Upload'
|
|||
|
|
|
|||
|
|
// 定义ASCII码中的空格和逗号字符的码点
|
|||
|
|
const ASCII_SPACE = ' '.codePointAt(0)
|
|||
|
|
const ASCII_COMMA = ','.codePointAt(0)
|
|||
|
|
// 定义用于验证Base64字符串的正则表达式
|
|||
|
|
const BASE64_REGEX = /^[\d+/A-Za-z]*={0,2}$/
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 验证元数据键的有效性
|
|||
|
|
* @param key 需要验证的键
|
|||
|
|
* @returns 如果键有效则返回true,否则返回false
|
|||
|
|
*/
|
|||
|
|
export function validateKey(key: string) {
|
|||
|
|
// 如果键的长度为0,则无效
|
|||
|
|
if (key.length === 0) {
|
|||
|
|
return false
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 遍历键的每个字符,检查其码点是否在有效范围内
|
|||
|
|
for (let i = 0; i < key.length; ++i) {
|
|||
|
|
const charCodePoint = key.codePointAt(i) as number
|
|||
|
|
if (
|
|||
|
|
charCodePoint > 127 || // 非ASCII字符
|
|||
|
|
charCodePoint === ASCII_SPACE || // 空格字符
|
|||
|
|
charCodePoint === ASCII_COMMA // 逗号字符
|
|||
|
|
) {
|
|||
|
|
return false
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return true
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 验证元数据值的有效性
|
|||
|
|
* @param value 需要验证的值
|
|||
|
|
* @returns 如果值是有效的Base64字符串则返回true,否则返回false
|
|||
|
|
*/
|
|||
|
|
export function validateValue(value: string) {
|
|||
|
|
// Base64字符串的长度必须是4的倍数
|
|||
|
|
if (value.length % 4 !== 0) {
|
|||
|
|
return false
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 使用正则表达式验证Base64字符串的格式
|
|||
|
|
return BASE64_REGEX.test(value)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 解析元数据字符串
|
|||
|
|
* @param str 需要解析的元数据字符串
|
|||
|
|
* @returns 返回解析后的元数据对象
|
|||
|
|
* @throws 如果元数据字符串无效则抛出错误
|
|||
|
|
*/
|
|||
|
|
export function parse(str?: string) {
|
|||
|
|
const meta: Record<string, string | null> = {}
|
|||
|
|
|
|||
|
|
// 如果字符串为空或仅包含空白字符,则无效
|
|||
|
|
if (!str || str.trim().length === 0) {
|
|||
|
|
throw new Error('Metadata string is not valid')
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 遍历字符串中的每个键值对
|
|||
|
|
for (const pair of str.split(',')) {
|
|||
|
|
const tokens = pair.split(' ')
|
|||
|
|
const [key, value] = tokens
|
|||
|
|
// 验证键和值的有效性,并确保键在元数据对象中不存在
|
|||
|
|
if (
|
|||
|
|
((tokens.length === 1 && validateKey(key)) ||
|
|||
|
|
(tokens.length === 2 && validateKey(key) && validateValue(value))) &&
|
|||
|
|
!(key in meta)
|
|||
|
|
) {
|
|||
|
|
// 如果值存在,则将其从Base64解码为UTF-8字符串
|
|||
|
|
const decodedValue = value ? Buffer.from(value, 'base64').toString('utf8') : null
|
|||
|
|
meta[key] = decodedValue
|
|||
|
|
} else {
|
|||
|
|
throw new Error('Metadata string is not valid')
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return meta
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 将元数据对象序列化为字符串
|
|||
|
|
* @param metadata 需要序列化的元数据对象
|
|||
|
|
* @returns 返回序列化后的元数据字符串
|
|||
|
|
*/
|
|||
|
|
export function stringify(metadata: NonNullable<Upload['metadata']>): string {
|
|||
|
|
return Object.entries(metadata)
|
|||
|
|
.map(([key, value]) => {
|
|||
|
|
// 如果值为null,则仅返回键
|
|||
|
|
if (value === null) {
|
|||
|
|
return key
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 将值编码为Base64字符串,并与键组合
|
|||
|
|
const encodedValue = Buffer.from(value, 'utf8').toString('base64')
|
|||
|
|
return `${key} ${encodedValue}`
|
|||
|
|
})
|
|||
|
|
.join(',')
|
|||
|
|
}
|