// Common API 相关接口 import { BASE_URL } from './constants' export interface ApiResponse { code: number successful: boolean message: 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 => { // 添加超时控制 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 => { 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) }) }