Files
jiapuapp/services/api/file-upload-contract.js

106 lines
4.5 KiB
JavaScript

import { createRequestError } from './request-client.js'
import { normalizeOptionalResponseText } from './response-normalizers.js'
import { assertPlainPayload, normalizeOptionalText } from './request-normalizers.js'
const normalizeFileResponseText = (value, field) =>
normalizeOptionalResponseText(value, field, '文件服务响应', 'FILE_RESPONSE_INVALID')
export const normalizeUploadId = (value, field) => {
if (typeof value !== 'string' || !value.trim()) throw new TypeError(`${field}必须是非空字符串`)
return value.trim()
}
const normalizeUploadFileName = (value) => {
if (typeof value !== 'string' || !value.trim()) throw new TypeError('fileName必须是非空字符串')
return value.trim()
}
export const normalizeUploadMd5 = (value, field) => {
if (typeof value !== 'string' || !/^[a-f0-9]{32}$/i.test(value)) {
throw new TypeError(`${field}必须是 32 位十六进制 MD5`)
}
return value.toLowerCase()
}
const normalizeUploadSize = (value, field) => {
if (!Number.isSafeInteger(value) || value <= 0) throw new TypeError(`${field}必须是正安全整数`)
return value
}
export const normalizeResumableInitPayload = (payload) => {
const allowedFields = new Set(['uploadId', 'fileName', 'fileMd5', 'totalSize', 'totalChunks', 'chunkSize', 'contentType'])
assertPlainPayload(payload, allowedFields, '文件初始化请求')
if (!Number.isInteger(payload.totalChunks) || payload.totalChunks <= 0 || payload.totalChunks > 2147483647) {
throw new TypeError('totalChunks必须是正 int32')
}
const normalizedPayload = {
uploadId: normalizeUploadId(payload.uploadId, 'uploadId'),
fileName: normalizeUploadFileName(payload.fileName),
fileMd5: normalizeUploadMd5(payload.fileMd5, 'fileMd5'),
totalSize: normalizeUploadSize(payload.totalSize, 'totalSize'),
totalChunks: payload.totalChunks,
chunkSize: normalizeUploadSize(payload.chunkSize, 'chunkSize')
}
const contentType = normalizeOptionalText(payload.contentType, 'contentType')
if (contentType) normalizedPayload.contentType = contentType
return normalizedPayload
}
export const normalizeResumableCompletePayload = (payload) => {
const allowedFields = new Set(['uploadId', 'fileName', 'fileMd5', 'totalSize', 'totalChunks'])
assertPlainPayload(payload, allowedFields, '文件完成请求')
if (!Number.isInteger(payload.totalChunks) || payload.totalChunks <= 0 || payload.totalChunks > 2147483647) {
throw new TypeError('totalChunks必须是正 int32')
}
return {
uploadId: normalizeUploadId(payload.uploadId, 'uploadId'),
fileName: normalizeUploadFileName(payload.fileName),
fileMd5: normalizeUploadMd5(payload.fileMd5, 'fileMd5'),
totalSize: normalizeUploadSize(payload.totalSize, 'totalSize'),
totalChunks: payload.totalChunks
}
}
const normalizeUploadOssId = (value, field) => {
const id = typeof value === 'number' && Number.isSafeInteger(value) ? String(value) : value
if (typeof id !== 'string' || !/^[1-9]\d*$/.test(id)) {
throw createRequestError(`文件服务字段 ${field} 无效`, 'FILE_RESPONSE_INVALID')
}
return id
}
export const normalizeResumableInitResult = (payload) => {
if (!payload || typeof payload !== 'object' || Array.isArray(payload)) {
throw createRequestError('文件初始化响应格式无效', 'FILE_RESPONSE_INVALID')
}
if (typeof payload.instant !== 'boolean') {
throw createRequestError('文件初始化响应缺少 instant', 'FILE_RESPONSE_INVALID')
}
const ossId = payload.ossId === undefined || payload.ossId === null ? null : normalizeUploadOssId(payload.ossId, 'ossId')
if (payload.instant && !ossId) {
throw createRequestError('秒传响应缺少 ossId', 'FILE_RESPONSE_INVALID')
}
const uploadId = payload.instant && (payload.uploadId === undefined || payload.uploadId === null)
? null
: normalizeUploadId(payload.uploadId, '响应 uploadId')
return {
uploadId,
instant: payload.instant,
ossId,
url: normalizeFileResponseText(payload.url, 'url'),
fileName: normalizeFileResponseText(payload.fileName, 'fileName')
}
}
export const normalizeResumableCompleteResult = (payload) => {
if (!payload || typeof payload !== 'object' || Array.isArray(payload)) {
throw createRequestError('文件完成响应格式无效', 'FILE_RESPONSE_INVALID')
}
return {
ossId: normalizeUploadOssId(payload.ossId, 'ossId'),
url: normalizeFileResponseText(payload.url, 'url'),
thumbnailUrl: normalizeFileResponseText(payload.thumbnailUrl, 'thumbnailUrl'),
fileName: normalizeFileResponseText(payload.fileName, 'fileName')
}
}