fix: 修复火山云API调用问题 - 添加文件上传到云端功能

问题修复:
- 火山云API需要HTTPS URL,不能使用本地文件路径
- 添加CloudUploadService集成,在调用API前先上传文件到云端
- 修复image_url和driving_video_url使用本地路径的问题

技术实现:
- 集成现有的CloudUploadService到VolcanoVideoService
- 在call_volcano_api方法中添加文件上传逻辑
- 先上传图片和驱动视频到云端,获取HTTPS URL
- 使用云端URL调用火山云API,确保API调用成功
- 添加详细的上传进度日志和错误处理

修复的错误:
- 400 Bad Request: image format unsupported: invalid image url
- 错误原因: 使用了本地文件路径 'c:\\Users\\...' 而非HTTPS URL
- 解决方案: 自动上传文件到云端并获取可访问的URL
This commit is contained in:
imeepos 2025-07-31 13:06:08 +08:00
parent 09b79d55df
commit 2d9e6f067d
2 changed files with 42 additions and 9 deletions

View File

@ -15,6 +15,7 @@ use crate::data::models::video_generation_record::{
};
use crate::data::repositories::video_generation_record_repository::VideoGenerationRecordRepository;
use crate::infrastructure::database::Database;
use crate::business::services::cloud_upload_service::CloudUploadService;
/// 火山云视频生成API响应提交任务
#[derive(Debug, Clone, Serialize, Deserialize)]
@ -88,6 +89,7 @@ pub struct VolcanoVideoService {
database: Arc<Database>,
repository: VideoGenerationRecordRepository,
http_client: Client,
cloud_upload_service: CloudUploadService,
vol_access_key: String,
vol_secret_key: String,
}
@ -108,6 +110,7 @@ impl VolcanoVideoService {
database,
repository,
http_client,
cloud_upload_service: CloudUploadService::new(),
vol_access_key,
vol_secret_key,
}
@ -210,19 +213,48 @@ impl VolcanoVideoService {
/// 调用火山云视频生成API
async fn call_volcano_api(&self, record: &VideoGenerationRecord) -> Result<VolcanoVideoGenerationResponse> {
let image_url = record.image_url.as_ref()
.ok_or_else(|| anyhow!("图片URL不能为空"))?;
// 获取本地文件路径
let image_path = record.image_url.as_ref()
.ok_or_else(|| anyhow!("图片路径不能为空"))?;
let driving_video_path = record.audio_url.as_ref()
.ok_or_else(|| anyhow!("驱动视频路径不能为空,请上传驱动视频文件"))?;
// 检查是否有驱动视频URL这个API需要驱动视频
let driving_video_url = record.audio_url.as_ref()
.ok_or_else(|| anyhow!("驱动视频URL不能为空请上传驱动视频文件"))?;
// 上传图片到云端
info!("正在上传图片到云端: {}", image_path);
let image_upload_result = self.cloud_upload_service
.upload_file(image_path, None, None)
.await?;
if !image_upload_result.success {
return Err(anyhow!("图片上传失败: {}",
image_upload_result.error_message.unwrap_or_default()));
}
let image_url = image_upload_result.remote_url
.ok_or_else(|| anyhow!("图片上传成功但未返回URL"))?;
// 上传驱动视频到云端
info!("正在上传驱动视频到云端: {}", driving_video_path);
let video_upload_result = self.cloud_upload_service
.upload_file(driving_video_path, None, None)
.await?;
if !video_upload_result.success {
return Err(anyhow!("驱动视频上传失败: {}",
video_upload_result.error_message.unwrap_or_default()));
}
let driving_video_url = video_upload_result.remote_url
.ok_or_else(|| anyhow!("驱动视频上传成功但未返回URL"))?;
info!("文件上传完成 - 图片: {}, 驱动视频: {}", image_url, driving_video_url);
let request_body = VolcanoVideoGenerationRequest {
req_key: "realman_avatar_imitator_v2v_gen_video".to_string(),
image_url: image_url.clone(),
image_url,
driving_video_info: DrivingVideoInfo {
store_type: 0, // 固定值
video_url: driving_video_url.clone(),
video_url: driving_video_url,
},
};
@ -543,6 +575,7 @@ impl Clone for VolcanoVideoService {
database: self.database.clone(),
repository: VideoGenerationRecordRepository::new(self.database.clone()),
http_client: self.http_client.clone(),
cloud_upload_service: CloudUploadService::new(),
vol_access_key: self.vol_access_key.clone(),
vol_secret_key: self.vol_secret_key.clone(),
}

View File

@ -446,8 +446,8 @@ const VideoGenerationTool: React.FC = () => {
)}
{record.audio_url && (
<span className="inline-flex items-center text-xs text-gray-500">
<FileAudio className="w-3 h-3 mr-1" />
<FileVideo className="w-3 h-3 mr-1" />
</span>
)}
</div>