refactor: 重构 Hedra 文件上传方式,使用文件路径而非文件内容传输
- 修改 HedraFileUploadRequest 使用 file_path 而不是 file_data - 添加 HedraFileUploadApiRequest 用于后端到 API 的请求 - 更新后端服务读取文件并转换为 API 请求格式 - 重构前端文件选择,使用 @tauri-apps/api/dialog 直接选择文件路径 - 移除复杂的文件转换和临时文件创建逻辑 - 简化文件上传流程,避免大文件在前后端间传输 这种方式更适合 Tauri 架构,避免了大文件传输的性能问题
This commit is contained in:
parent
538254ee38
commit
5fdf3c5a4b
|
|
@ -258,12 +258,19 @@ pub struct ComfyUISyncExecuteRequest {
|
|||
pub max_wait_time: Option<u32>,
|
||||
}
|
||||
|
||||
/// Hedra 文件上传请求
|
||||
/// Hedra 文件上传请求(前端到后端)
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct HedraFileUploadRequest {
|
||||
pub file_path: String,
|
||||
pub purpose: Option<String>, // 'image', 'audio', 'video', 'voice'
|
||||
}
|
||||
|
||||
/// Hedra 文件上传 API 请求(后端到 API)
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct HedraFileUploadApiRequest {
|
||||
pub file_data: Vec<u8>,
|
||||
pub filename: String,
|
||||
pub purpose: Option<String>, // 'image', 'audio', 'video', 'voice'
|
||||
pub purpose: Option<String>,
|
||||
}
|
||||
|
||||
/// Hedra 任务提交请求
|
||||
|
|
|
|||
|
|
@ -525,7 +525,25 @@ impl BowongTextVideoAgentService {
|
|||
|
||||
/// Hedra 上传文件
|
||||
pub async fn hedra_upload_file(&self, request: &HedraFileUploadRequest) -> Result<FileUploadResponse> {
|
||||
self.execute_request("hedra_upload_file", Some(request)).await
|
||||
// 读取文件内容
|
||||
let file_data = std::fs::read(&request.file_path)
|
||||
.map_err(|e| anyhow!("Failed to read file {}: {}", request.file_path, e))?;
|
||||
|
||||
// 获取文件名
|
||||
let filename = std::path::Path::new(&request.file_path)
|
||||
.file_name()
|
||||
.and_then(|name| name.to_str())
|
||||
.ok_or_else(|| anyhow!("Invalid file path: {}", request.file_path))?
|
||||
.to_string();
|
||||
|
||||
// 构造 API 请求
|
||||
let api_request = HedraFileUploadApiRequest {
|
||||
file_data,
|
||||
filename,
|
||||
purpose: request.purpose.clone(),
|
||||
};
|
||||
|
||||
self.execute_request("hedra_upload_file", Some(&api_request)).await
|
||||
}
|
||||
|
||||
/// Hedra 提交任务
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ import {
|
|||
FileImage,
|
||||
FileAudio
|
||||
} from 'lucide-react';
|
||||
import { open } from '@tauri-apps/api/dialog';
|
||||
import { useNotifications } from '../../components/NotificationSystem';
|
||||
import { createBowongTextVideoAgentService } from '../../services/bowongTextVideoAgentService';
|
||||
import {
|
||||
|
|
@ -104,14 +105,29 @@ const HedraLipSyncTool: React.FC = () => {
|
|||
}
|
||||
}, [addNotification]);
|
||||
|
||||
// 文件上传
|
||||
const uploadFile = useCallback(async (fileInfo: HedraFileInfo, purpose: 'image' | 'audio'): Promise<string> => {
|
||||
const request: HedraFileUploadRequest = {
|
||||
local_file: fileInfo.file,
|
||||
purpose
|
||||
};
|
||||
|
||||
// 文件选择和上传
|
||||
const selectAndUploadFile = useCallback(async (purpose: 'image' | 'audio'): Promise<string> => {
|
||||
try {
|
||||
// 使用文件选择对话框
|
||||
const selected = await open({
|
||||
multiple: false,
|
||||
filters: [{
|
||||
name: purpose === 'image' ? '图片文件' : '音频文件',
|
||||
extensions: purpose === 'image'
|
||||
? ['jpg', 'jpeg', 'png', 'gif', 'bmp', 'webp']
|
||||
: ['mp3', 'wav', 'aac', 'flac', 'm4a', 'ogg']
|
||||
}]
|
||||
});
|
||||
|
||||
if (!selected || typeof selected !== 'string') {
|
||||
throw new Error('未选择文件');
|
||||
}
|
||||
|
||||
const request: HedraFileUploadRequest = {
|
||||
file_path: selected,
|
||||
purpose
|
||||
};
|
||||
|
||||
const response = await bowongService.hedraUploadFile(request);
|
||||
if (response.status && response.data) {
|
||||
return response.data;
|
||||
|
|
@ -119,7 +135,7 @@ const HedraLipSyncTool: React.FC = () => {
|
|||
throw new Error(response.msg || '文件上传失败');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('文件上传失败:', error);
|
||||
console.error('文件选择和上传失败:', error);
|
||||
throw error;
|
||||
}
|
||||
}, []);
|
||||
|
|
@ -144,7 +160,7 @@ const HedraLipSyncTool: React.FC = () => {
|
|||
imageFile: prev.imageFile ? { ...prev.imageFile, uploadStatus: 'uploading' } : null
|
||||
}));
|
||||
|
||||
const imageUrl = await uploadFile(state.imageFile, 'image');
|
||||
const imageUrl = await selectAndUploadFile('image');
|
||||
|
||||
setState(prev => ({
|
||||
...prev,
|
||||
|
|
@ -157,7 +173,7 @@ const HedraLipSyncTool: React.FC = () => {
|
|||
audioFile: prev.audioFile ? { ...prev.audioFile, uploadStatus: 'uploading' } : null
|
||||
}));
|
||||
|
||||
const audioUrl = await uploadFile(state.audioFile, 'audio');
|
||||
const audioUrl = await selectAndUploadFile('audio');
|
||||
|
||||
setState(prev => ({
|
||||
...prev,
|
||||
|
|
@ -217,7 +233,7 @@ const HedraLipSyncTool: React.FC = () => {
|
|||
message: error instanceof Error ? error.message : '口型合成任务失败'
|
||||
});
|
||||
}
|
||||
}, [state.imageFile, state.audioFile, uploadFile, addNotification]);
|
||||
}, [selectAndUploadFile, addNotification]);
|
||||
|
||||
// 轮询任务状态
|
||||
const pollTaskStatus = useCallback(async (taskId: string) => {
|
||||
|
|
|
|||
|
|
@ -606,15 +606,7 @@ export class BowongTextVideoAgentFastApiService implements BowongTextVideoAgentA
|
|||
// ============================================================================
|
||||
|
||||
async hedraUploadFile(request: HedraFileUploadRequest): Promise<FileUploadResponse> {
|
||||
// 将 File 对象转换为后端期望的格式
|
||||
const fileData = await this.fileToBytes(request.local_file);
|
||||
const backendRequest = {
|
||||
file_data: Array.from(fileData), // 转换为数字数组
|
||||
filename: request.local_file.name,
|
||||
purpose: request.purpose || 'image'
|
||||
};
|
||||
|
||||
return this.invokeAPI<FileUploadResponse>('hedra_upload_file', backendRequest);
|
||||
return this.invokeAPI<FileUploadResponse>('hedra_upload_file', request);
|
||||
}
|
||||
|
||||
async hedraSubmitTask(request: HedraTaskSubmitRequest): Promise<TaskResponse> {
|
||||
|
|
@ -641,23 +633,7 @@ export class BowongTextVideoAgentFastApiService implements BowongTextVideoAgentA
|
|||
// 工具方法
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* 将 File 对象转换为字节数组
|
||||
*/
|
||||
private async fileToBytes(file: File): Promise<Uint8Array> {
|
||||
return new Promise((resolve, reject) => {
|
||||
const reader = new FileReader();
|
||||
reader.onload = () => {
|
||||
if (reader.result instanceof ArrayBuffer) {
|
||||
resolve(new Uint8Array(reader.result));
|
||||
} else {
|
||||
reject(new Error('Failed to read file as ArrayBuffer'));
|
||||
}
|
||||
};
|
||||
reader.onerror = () => reject(reader.error);
|
||||
reader.readAsArrayBuffer(file);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取服务配置
|
||||
|
|
|
|||
|
|
@ -545,7 +545,7 @@ export interface ComfyUISyncExecuteRequest {
|
|||
* Hedra 文件上传请求
|
||||
*/
|
||||
export interface HedraFileUploadRequest {
|
||||
local_file: File;
|
||||
file_path: string;
|
||||
purpose?: 'image' | 'audio' | 'video' | 'voice'; // 默认 'image'
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue