fix(cors): 修复H5环境下的CORS上传问题
- 在config/dev.ts中添加代理配置,将/api/*请求代理到外部API服务器 - 在bowongAISDK.ts中添加平台检测逻辑,H5环境使用fetch避免CORS - 新增H5专用的文件上传和图像生成方法 - 保持小程序环境使用原有的Taro.uploadFile方法 - 确保跨平台兼容性,解决XMLHttpRequest凭据模式与通配符CORS头冲突 修复错误: - Access-Control-Allow-Origin头在凭据模式下不能使用通配符* - H5环境下uploadFile自动设置withCredentials导致的CORS阻止
This commit is contained in:
parent
6c4247c8ea
commit
595e56378c
|
|
@ -7,6 +7,24 @@ export default {
|
||||||
devServer: {
|
devServer: {
|
||||||
fs: {
|
fs: {
|
||||||
allow: ['..']
|
allow: ['..']
|
||||||
|
},
|
||||||
|
proxy: {
|
||||||
|
'/api': {
|
||||||
|
target: 'https://bowongai-test--text-video-agent-fastapi-app.modal.run',
|
||||||
|
changeOrigin: true,
|
||||||
|
secure: true,
|
||||||
|
configure: (proxy, _options) => {
|
||||||
|
proxy.on('error', (err, _req, _res) => {
|
||||||
|
console.log('proxy error', err);
|
||||||
|
});
|
||||||
|
proxy.on('proxyReq', (proxyReq, req, _res) => {
|
||||||
|
console.log('Sending Request to the Target:', req.method, req.url);
|
||||||
|
});
|
||||||
|
proxy.on('proxyRes', (proxyRes, req, _res) => {
|
||||||
|
console.log('Received Response from the Target:', proxyRes.statusCode, req.url);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -62,6 +62,152 @@ export class BowongAISDK {
|
||||||
/** API 服务基础地址 */
|
/** API 服务基础地址 */
|
||||||
private readonly baseUrl = 'https://bowongai-test--text-video-agent-fastapi-app.modal.run';
|
private readonly baseUrl = 'https://bowongai-test--text-video-agent-fastapi-app.modal.run';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查当前是否为H5平台
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
private _isH5Platform(): boolean {
|
||||||
|
return process.env.TARO_ENV === 'h5';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* H5平台专用的文件上传方法
|
||||||
|
* 使用Taro.request避免CORS问题
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
private async _uploadFileForH5(params: UploadParams): Promise<string> {
|
||||||
|
const { filePath, name, type, onProgress } = params;
|
||||||
|
const url = `${this._isH5Platform() ? '' : this.baseUrl}/api/file/upload/s3`;
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 获取文件信息
|
||||||
|
let fileInfo;
|
||||||
|
try {
|
||||||
|
fileInfo = await Taro.getFileInfo({ filePath });
|
||||||
|
if (!isGetFileInfoSuccessCallbackResult(fileInfo)) {
|
||||||
|
console.warn('获取文件信息失败,使用默认值');
|
||||||
|
fileInfo = { size: 0 };
|
||||||
|
}
|
||||||
|
} catch (getFileInfoError) {
|
||||||
|
console.warn('getFileInfo调用失败,使用默认值:', getFileInfoError);
|
||||||
|
fileInfo = { size: 0 };
|
||||||
|
}
|
||||||
|
|
||||||
|
// 自动生成文件名(如果未提供)
|
||||||
|
const fileName = name || `upload_${Date.now()}.${this._getFileExtension(filePath)}`;
|
||||||
|
// 自动判断文件类型(如果未提供)
|
||||||
|
const fileType = type || this._getMimeType(filePath);
|
||||||
|
|
||||||
|
console.log('开始上传文件 (H5模式):', {
|
||||||
|
fileName,
|
||||||
|
fileSize: fileInfo.size,
|
||||||
|
fileType
|
||||||
|
});
|
||||||
|
|
||||||
|
// 创建FormData
|
||||||
|
const formData = new FormData();
|
||||||
|
|
||||||
|
// 在H5环境中,需要先将filePath转换为File对象
|
||||||
|
const response = await fetch(filePath);
|
||||||
|
const blob = await response.blob();
|
||||||
|
const file = new File([blob], fileName, { type: fileType });
|
||||||
|
|
||||||
|
formData.append('file', file);
|
||||||
|
formData.append('filename', fileName);
|
||||||
|
formData.append('type', fileType);
|
||||||
|
|
||||||
|
// 使用fetch进行上传
|
||||||
|
const uploadResponse = await fetch(url, {
|
||||||
|
method: 'POST',
|
||||||
|
body: formData,
|
||||||
|
headers: {
|
||||||
|
'Accept': 'application/json'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!uploadResponse.ok) {
|
||||||
|
throw new Error(`HTTP ${uploadResponse.status}: 文件上传失败`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const result: ApiResponse<string> = await uploadResponse.json();
|
||||||
|
|
||||||
|
if (!result.status) {
|
||||||
|
throw new Error(result.msg || '文件上传失败');
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('文件上传成功 (H5模式):', result.data);
|
||||||
|
return result.data;
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error('H5文件上传失败:', error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* H5平台专用的图像生成方法
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
private async _generateImageForH5(params: GenerateImageParams): Promise<string> {
|
||||||
|
const {
|
||||||
|
prompt,
|
||||||
|
model_name = 'gemini-2.5-flash-image-preview',
|
||||||
|
aspect_ratio = '9:16',
|
||||||
|
mode = 'turbo',
|
||||||
|
webhook_flag = false,
|
||||||
|
img_file
|
||||||
|
} = params;
|
||||||
|
|
||||||
|
const url = `${this._isH5Platform() ? '' : this.baseUrl}/api/custom/image/submit/task`;
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (img_file) {
|
||||||
|
// 创建FormData
|
||||||
|
const formData = new FormData();
|
||||||
|
|
||||||
|
// 在H5环境中,需要先将filePath转换为File对象
|
||||||
|
const response = await fetch(img_file);
|
||||||
|
const blob = await response.blob();
|
||||||
|
const fileName = `generate_${Date.now()}.${this._getFileExtension(img_file)}`;
|
||||||
|
const file = new File([blob], fileName, { type: this._getMimeType(img_file) });
|
||||||
|
|
||||||
|
formData.append('img_file', file);
|
||||||
|
formData.append('prompt', prompt);
|
||||||
|
formData.append('model_name', model_name);
|
||||||
|
formData.append('aspect_ratio', aspect_ratio);
|
||||||
|
formData.append('mode', mode.toString());
|
||||||
|
formData.append('webhook_flag', webhook_flag.toString());
|
||||||
|
formData.append('img_url', '');
|
||||||
|
|
||||||
|
// 使用fetch进行上传
|
||||||
|
const uploadResponse = await fetch(url, {
|
||||||
|
method: 'POST',
|
||||||
|
body: formData,
|
||||||
|
headers: {
|
||||||
|
'accept': 'application/json'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!uploadResponse.ok) {
|
||||||
|
throw new Error(`HTTP ${uploadResponse.status}: 图像生成请求失败`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const result: ApiResponse<string> = await uploadResponse.json();
|
||||||
|
|
||||||
|
if (!result.status) {
|
||||||
|
throw new Error(result.msg || '图像生成请求失败');
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('图像生成任务提交成功 (H5模式):', result.data);
|
||||||
|
return result.data;
|
||||||
|
}
|
||||||
|
throw new Error(`请选择图片`)
|
||||||
|
} catch (error) {
|
||||||
|
console.error('H5图像生成失败:', error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 上传文件到 S3
|
* 上传文件到 S3
|
||||||
|
|
@ -70,14 +216,26 @@ export class BowongAISDK {
|
||||||
* @returns Promise<string> 返回上传后的文件地址
|
* @returns Promise<string> 返回上传后的文件地址
|
||||||
*/
|
*/
|
||||||
async upload(params: UploadParams): Promise<string> {
|
async upload(params: UploadParams): Promise<string> {
|
||||||
|
// 如果是H5平台,使用专用的上传方法
|
||||||
|
if (this._isH5Platform()) {
|
||||||
|
return this._uploadFileForH5(params);
|
||||||
|
}
|
||||||
|
|
||||||
const { filePath, name, type, onProgress } = params;
|
const { filePath, name, type, onProgress } = params;
|
||||||
const url = `${this.baseUrl}/api/file/upload/s3`;
|
const url = `${this.baseUrl}/api/file/upload/s3`;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// 获取文件信息
|
// 获取文件信息
|
||||||
const fileInfo = await Taro.getFileInfo({ filePath });
|
let fileInfo;
|
||||||
if (!isGetFileInfoSuccessCallbackResult(fileInfo)) {
|
try {
|
||||||
throw new Error(fileInfo.errMsg)
|
fileInfo = await Taro.getFileInfo({ filePath });
|
||||||
|
if (!isGetFileInfoSuccessCallbackResult(fileInfo)) {
|
||||||
|
console.warn('获取文件信息失败,使用默认值');
|
||||||
|
fileInfo = { size: 0 };
|
||||||
|
}
|
||||||
|
} catch (getFileInfoError) {
|
||||||
|
console.warn('getFileInfo调用失败,使用默认值:', getFileInfoError);
|
||||||
|
fileInfo = { size: 0 };
|
||||||
}
|
}
|
||||||
// 自动生成文件名(如果未提供)
|
// 自动生成文件名(如果未提供)
|
||||||
const fileName = name || `upload_${Date.now()}.${this._getFileExtension(filePath)}`;
|
const fileName = name || `upload_${Date.now()}.${this._getFileExtension(filePath)}`;
|
||||||
|
|
@ -401,6 +559,11 @@ export class BowongAISDK {
|
||||||
* -F 'img_file='
|
* -F 'img_file='
|
||||||
*/
|
*/
|
||||||
async generateImage(params: GenerateImageParams): Promise<string> {
|
async generateImage(params: GenerateImageParams): Promise<string> {
|
||||||
|
// 如果是H5平台,使用专用的生成方法
|
||||||
|
if (this._isH5Platform()) {
|
||||||
|
return this._generateImageForH5(params);
|
||||||
|
}
|
||||||
|
|
||||||
const {
|
const {
|
||||||
prompt,
|
prompt,
|
||||||
model_name = 'gemini-2.5-flash-image-preview',
|
model_name = 'gemini-2.5-flash-image-preview',
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue