forked from 77media/video-flow
211 lines
6.1 KiB
TypeScript
211 lines
6.1 KiB
TypeScript
// Common API 相关接口
|
||
import { BASE_URL } from './constants'
|
||
|
||
export interface ApiResponse<T = any> {
|
||
code: number
|
||
successful: boolean
|
||
msg: string
|
||
data: T
|
||
}
|
||
|
||
export interface TokenResponse {
|
||
code: number
|
||
message: string
|
||
data: {
|
||
token: string
|
||
}
|
||
successfull: boolean
|
||
}
|
||
|
||
export interface TokenWithDomainResponse {
|
||
code: number
|
||
message: string
|
||
data: {
|
||
token: string
|
||
domain: string
|
||
}
|
||
successfull: boolean
|
||
}
|
||
|
||
export interface QiniuUploadResponse {
|
||
hash: string
|
||
key: string
|
||
url?: string
|
||
}
|
||
|
||
|
||
|
||
// 获取七牛云上传token
|
||
export const getUploadToken = async (timeoutMs: number = 10000): Promise<string> => {
|
||
// 添加超时控制
|
||
const controller = new AbortController()
|
||
const timeoutId = setTimeout(() => {
|
||
console.log(`请求超时(${timeoutMs / 1000}秒),正在中断请求...`)
|
||
controller.abort()
|
||
}, timeoutMs)
|
||
|
||
try {
|
||
const response = await fetch(`${BASE_URL}/common/get-upload-token`, {
|
||
method: "GET",
|
||
headers: {
|
||
Accept: "application/json",
|
||
"Content-Type": "application/json",
|
||
},
|
||
signal: controller.signal,
|
||
mode: "cors",
|
||
})
|
||
|
||
// 请求完成后清除超时
|
||
clearTimeout(timeoutId)
|
||
|
||
if (!response.ok) {
|
||
const errorText = await response.text()
|
||
console.error("获取token响应错误:", response.status, errorText)
|
||
throw new Error(`获取token失败: ${response.status} ${response.statusText}`)
|
||
}
|
||
|
||
const result: TokenResponse = await response.json()
|
||
console.log("获取token响应:", result)
|
||
|
||
if (result.code === 0 && result.successfull && result.data.token) {
|
||
console.log("成功获取token")
|
||
return result.data.token
|
||
} else {
|
||
throw new Error(result.message || "获取token失败,服务器未返回有效token")
|
||
}
|
||
} catch (error) {
|
||
clearTimeout(timeoutId)
|
||
console.error("获取上传token失败:", error)
|
||
|
||
if (error instanceof Error) {
|
||
if (error.name === "AbortError") {
|
||
throw new Error("请求超时,请检查网络连接")
|
||
} else if (error.message.includes("Failed to fetch")) {
|
||
throw new Error("网络连接失败,可能是CORS策略限制或服务器不可达")
|
||
}
|
||
}
|
||
|
||
throw error
|
||
}
|
||
}
|
||
|
||
// 获取七牛云上传token(包含domain)
|
||
export const getUploadTokenWithDomain = async (timeoutMs: number = 10000): Promise<{ token: string, domain: string }> => {
|
||
// 添加超时控制
|
||
const controller = new AbortController()
|
||
const timeoutId = setTimeout(() => {
|
||
console.log(`请求超时(${timeoutMs / 1000}秒),正在中断请求...`)
|
||
controller.abort()
|
||
}, timeoutMs)
|
||
|
||
try {
|
||
const response = await fetch(`${BASE_URL}/common/get-upload-token`, {
|
||
method: "GET",
|
||
headers: {
|
||
Accept: "application/json",
|
||
"Content-Type": "application/json",
|
||
},
|
||
signal: controller.signal,
|
||
mode: "cors",
|
||
})
|
||
|
||
// 请求完成后清除超时
|
||
clearTimeout(timeoutId)
|
||
|
||
if (!response.ok) {
|
||
const errorText = await response.text()
|
||
console.error("获取token响应错误:", response.status, errorText)
|
||
throw new Error(`获取token失败: ${response.status} ${response.statusText}`)
|
||
}
|
||
|
||
const result: TokenWithDomainResponse | TokenResponse = await response.json()
|
||
console.log("获取token响应:", result)
|
||
|
||
if (result.code === 0 && result.successfull && result.data.token) {
|
||
console.log("成功获取token")
|
||
// Support both old and new API response formats
|
||
const domain = 'domain' in result.data ? result.data.domain : 'cdn.qikongjian.com'
|
||
return {
|
||
token: result.data.token,
|
||
domain: domain
|
||
}
|
||
} else {
|
||
throw new Error(result.message || "获取token失败,服务器未返回有效token")
|
||
}
|
||
} catch (error) {
|
||
clearTimeout(timeoutId)
|
||
console.error("获取上传token失败:", error)
|
||
|
||
if (error instanceof Error) {
|
||
if (error.name === "AbortError") {
|
||
throw new Error("请求超时,请检查网络连接")
|
||
} else if (error.message.includes("Failed to fetch")) {
|
||
throw new Error("网络连接失败,可能是CORS策略限制或服务器不可达")
|
||
}
|
||
}
|
||
|
||
throw error
|
||
}
|
||
}
|
||
|
||
// 生成唯一文件名
|
||
export const generateUniqueFileName = (originalName: string): string => {
|
||
const timestamp = Date.now()
|
||
const randomStr = Math.random().toString(36).substring(2, 8)
|
||
const extension = originalName.split(".").pop()
|
||
return `videos/${timestamp}_${randomStr}.${extension}`
|
||
}
|
||
|
||
// 七牛云上传
|
||
export const uploadToQiniu = async (
|
||
file: File,
|
||
token: string,
|
||
onProgress?: (progress: number) => void
|
||
): Promise<string> => {
|
||
const uniqueFileName = generateUniqueFileName(file.name)
|
||
|
||
const formData = new FormData()
|
||
formData.append("token", token)
|
||
formData.append("key", uniqueFileName)
|
||
formData.append("file", file)
|
||
|
||
return new Promise((resolve, reject) => {
|
||
const xhr = new XMLHttpRequest()
|
||
|
||
xhr.upload.addEventListener("progress", (event) => {
|
||
if (event.lengthComputable && onProgress) {
|
||
const progress = Math.round((event.loaded / event.total) * 100)
|
||
onProgress(progress)
|
||
}
|
||
})
|
||
|
||
xhr.addEventListener("load", () => {
|
||
if (xhr.status >= 200 && xhr.status < 300) {
|
||
try {
|
||
const response: QiniuUploadResponse = JSON.parse(xhr.responseText)
|
||
const qiniuUrl = `https://cdn.qikongjian.com/${response.key || uniqueFileName}`
|
||
console.log("七牛云上传成功:", response)
|
||
resolve(qiniuUrl)
|
||
} catch (error) {
|
||
console.error("解析响应失败:", error, "原始响应:", xhr.responseText)
|
||
reject(new Error(`解析上传响应失败: ${xhr.responseText}`))
|
||
}
|
||
} else {
|
||
console.error("七牛云上传失败:", xhr.status, xhr.statusText, xhr.responseText)
|
||
reject(new Error(`上传失败: ${xhr.status} ${xhr.statusText}`))
|
||
}
|
||
})
|
||
|
||
xhr.addEventListener("error", (e) => {
|
||
console.error("上传网络错误:", e)
|
||
reject(new Error("网络错误,上传失败"))
|
||
})
|
||
|
||
xhr.addEventListener("abort", () => {
|
||
reject(new Error("上传被取消"))
|
||
})
|
||
|
||
xhr.open("POST", "https://up-z2.qiniup.com")
|
||
xhr.send(formData)
|
||
})
|
||
}
|