forked from 77media/video-flow
267 lines
7.0 KiB
TypeScript
267 lines
7.0 KiB
TypeScript
import axios, { AxiosInstance, AxiosRequestConfig, AxiosResponse, InternalAxiosRequestConfig, AxiosHeaders } from 'axios';
|
|
import { BASE_URL } from './constants'
|
|
// 创建 axios 实例
|
|
const request: AxiosInstance = axios.create({
|
|
baseURL: BASE_URL, // 设置基础URL
|
|
timeout: 300000, // 请求超时时间
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
},
|
|
});
|
|
|
|
// 请求拦截器
|
|
request.interceptors.request.use(
|
|
(config: InternalAxiosRequestConfig) => {
|
|
// 从 localStorage 获取 token
|
|
const token = localStorage?.getItem('token') || 'mock-token';
|
|
if (token && config.headers) {
|
|
(config.headers as AxiosHeaders).set('Authorization', `Bearer ${token}`);
|
|
}
|
|
return config;
|
|
},
|
|
(error) => {
|
|
return Promise.reject(error);
|
|
}
|
|
);
|
|
|
|
// 响应拦截器
|
|
request.interceptors.response.use(
|
|
(response: AxiosResponse) => {
|
|
// 直接返回响应数据
|
|
console.log('?????????????????????????',Object.keys(response.data));
|
|
|
|
return response.data;
|
|
},
|
|
(error) => {
|
|
if (error.response) {
|
|
switch (error.response.status) {
|
|
case 401:
|
|
// 未授权,清除 token 并跳转到登录页
|
|
localStorage?.removeItem('token');
|
|
window.location.href = '/login';
|
|
break;
|
|
case 403:
|
|
// 权限不足
|
|
console.error('没有权限访问该资源');
|
|
break;
|
|
case 404:
|
|
// 资源不存在
|
|
console.error('请求的资源不存在');
|
|
break;
|
|
case 500:
|
|
// 服务器错误
|
|
console.error('服务器错误');
|
|
break;
|
|
default:
|
|
console.error('请求失败:', error.response.data.message || '未知错误');
|
|
}
|
|
} else if (error.request) {
|
|
// 请求已发出但没有收到响应
|
|
console.error('网络错误,请检查您的网络连接');
|
|
} else {
|
|
// 请求配置出错
|
|
console.error('请求配置错误:', error.message);
|
|
}
|
|
return Promise.reject(error);
|
|
}
|
|
);
|
|
|
|
// 封装 GET 请求
|
|
export const get = <T>(url: string, config?: AxiosRequestConfig): Promise<T> => {
|
|
return request.get(url, config);
|
|
};
|
|
|
|
// 封装 POST 请求
|
|
export const post = <T>(url: string, data?: any, config?: AxiosRequestConfig): Promise<T> => {
|
|
return request.post(url, data, config);
|
|
};
|
|
|
|
// 封装 PUT 请求
|
|
export const put = <T>(url: string, data?: any, config?: AxiosRequestConfig): Promise<T> => {
|
|
return request.put(url, data, config);
|
|
};
|
|
|
|
// 封装 DELETE 请求
|
|
export const del = <T>(url: string, config?: AxiosRequestConfig): Promise<T> => {
|
|
return request.delete(url, config);
|
|
};
|
|
|
|
// utils/streamJsonPost.ts
|
|
export async function streamJsonPost<T = any>(
|
|
url: string,
|
|
body: any,
|
|
onJson: (json: T) => void
|
|
) {
|
|
try {
|
|
const token = localStorage?.getItem('token') || 'mock-token';
|
|
const response = await fetch(`${BASE_URL}${url}`, {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
'Authorization': `Bearer ${token}`
|
|
},
|
|
body: JSON.stringify(body),
|
|
});
|
|
|
|
if (!response.ok) {
|
|
throw new Error(`HTTP error! status: ${response.status}`);
|
|
}
|
|
|
|
if (!response.body) {
|
|
throw new Error('Stream not supported');
|
|
}
|
|
|
|
const reader = response.body.getReader();
|
|
const decoder = new TextDecoder('utf-8');
|
|
let buffer = '';
|
|
|
|
try {
|
|
while (true) {
|
|
const { done, value } = await reader.read();
|
|
|
|
if (done) {
|
|
// Process any remaining data in the buffer
|
|
if (buffer.trim()) {
|
|
try {
|
|
const parsed = JSON.parse(buffer.trim());
|
|
onJson(parsed);
|
|
} catch (err) {
|
|
console.warn('Final JSON parse error:', err, buffer);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
// Decode the current chunk and add to buffer
|
|
buffer += decoder.decode(value, { stream: true });
|
|
|
|
// Process complete JSON objects
|
|
let boundary = buffer.indexOf('\n');
|
|
while (boundary !== -1) {
|
|
const chunk = buffer.slice(0, boundary).trim();
|
|
buffer = buffer.slice(boundary + 1);
|
|
|
|
if (chunk) {
|
|
try {
|
|
const parsed = JSON.parse(chunk);
|
|
onJson(parsed);
|
|
} catch (err) {
|
|
// Only log if it's not an empty line
|
|
if (chunk !== '') {
|
|
console.warn('JSON parse error:', err, chunk);
|
|
}
|
|
}
|
|
}
|
|
boundary = buffer.indexOf('\n');
|
|
}
|
|
}
|
|
} catch (error) {
|
|
console.error('Stream processing error:', error);
|
|
throw error;
|
|
} finally {
|
|
// Ensure reader is released
|
|
reader.releaseLock();
|
|
}
|
|
} catch (error) {
|
|
console.error('Stream request error:', error);
|
|
// Handle specific error types
|
|
if (error instanceof TypeError) {
|
|
console.error('Network error - check your connection');
|
|
}
|
|
if (error instanceof SyntaxError) {
|
|
console.error('Invalid JSON in the stream');
|
|
}
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
// 封装流式数据请求
|
|
export const stream = async <T>({
|
|
url,
|
|
method = 'POST',
|
|
data,
|
|
onMessage,
|
|
onError,
|
|
onComplete,
|
|
}: {
|
|
url: string;
|
|
method?: 'GET' | 'POST';
|
|
data?: any;
|
|
onMessage: (data: T) => void;
|
|
onError?: (error: any) => void;
|
|
onComplete?: () => void;
|
|
}) => {
|
|
try {
|
|
const config: AxiosRequestConfig = {
|
|
url,
|
|
method,
|
|
data,
|
|
responseType: 'stream',
|
|
headers: {
|
|
'Accept': 'text/event-stream',
|
|
'Cache-Control': 'no-cache',
|
|
'Connection': 'keep-alive',
|
|
},
|
|
onDownloadProgress: (progressEvent: any) => {
|
|
const responseText = progressEvent.event?.target?.responseText;
|
|
if (!responseText) return;
|
|
|
|
// 处理接收到的数据
|
|
const lines: string[] = responseText.split('\n');
|
|
lines.forEach((line: string) => {
|
|
if (line.startsWith('data: ')) {
|
|
try {
|
|
const data = JSON.parse(line.slice(6));
|
|
onMessage(data);
|
|
} catch (e) {
|
|
console.warn('Failed to parse stream data:', e);
|
|
}
|
|
}
|
|
});
|
|
},
|
|
};
|
|
|
|
const response = await request(config);
|
|
onComplete?.();
|
|
return response;
|
|
} catch (error) {
|
|
onError?.(error);
|
|
throw error;
|
|
}
|
|
};
|
|
|
|
// 封装文件下载流请求
|
|
export const downloadStream = async (
|
|
url: string,
|
|
filename: string,
|
|
config?: AxiosRequestConfig
|
|
) => {
|
|
try {
|
|
const response = await request({
|
|
url,
|
|
method: 'GET',
|
|
responseType: 'blob',
|
|
...config,
|
|
});
|
|
|
|
// 创建下载链接
|
|
const blob = new Blob([response.data], { type: response.headers['content-type'] });
|
|
const downloadUrl = window.URL.createObjectURL(blob);
|
|
const link = document.createElement('a');
|
|
link.href = downloadUrl;
|
|
link.download = filename;
|
|
document.body.appendChild(link);
|
|
link.click();
|
|
document.body.removeChild(link);
|
|
window.URL.revokeObjectURL(downloadUrl);
|
|
|
|
return response;
|
|
} catch (error) {
|
|
console.error('文件下载失败:', error);
|
|
throw error;
|
|
}
|
|
};
|
|
|
|
// 导出 request 实例
|
|
export default request;
|