AI返回模板内容"由于未接收到实际图片内容",说明AI无法访问通过URL传递的图片。
图片URL访问权限限制
completionJSON可能不支持URL方式
ai-k12-daofa成功使用vision,但其图片可能是公开可访问的将图片URL转换为base64格式,直接在请求中传递图片数据,绕过URL访问权限问题。
文件: design-analysis-ai.service.ts
// 🔥 关键修复:将图片URL转换为base64格式
console.log('🔄 开始将图片URL转换为base64...');
const base64Images: string[] = [];
for (let i = 0; i < options.images.length; i++) {
const url = options.images[i];
try {
console.log(`📥 下载图片${i + 1}: ${url}`);
// 1. 使用fetch下载图片
const response = await fetch(url);
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
}
// 2. 转换为Blob
const blob = await response.blob();
console.log(`📦 图片${i + 1}大小: ${(blob.size / 1024 / 1024).toFixed(2)}MB`);
// 3. 使用FileReader转换为base64
const base64 = await new Promise<string>((resolve, reject) => {
const reader = new FileReader();
reader.onloadend = () => resolve(reader.result as string);
reader.onerror = reject;
reader.readAsDataURL(blob);
});
base64Images.push(base64);
console.log(`✅ 图片${i + 1}已转换为base64 (${(base64.length / 1024).toFixed(2)}KB)`);
} catch (error: any) {
console.error(`❌ 图片${i + 1}转换失败:`, error);
throw new Error(`图片${i + 1}无法访问: ${error.message}\n请确保图片URL可公开访问`);
}
}
console.log(`✅ 所有图片已转换为base64,共${base64Images.length}张`);
// 4. 使用base64图片调用AI
const result = await completionJSON(
prompt,
'',
undefined,
2,
{
model: this.AI_MODEL,
vision: true,
images: base64Images, // 🔥 传入base64而非URL
max_tokens: 8000
}
);
用户上传图片
↓
文件上传到OBS存储
↓
获取图片URL (https://file-cloud.fmode.cn/...)
↓
【AI分析阶段】
↓
使用fetch下载图片 → Blob
↓
FileReader转换 → base64字符串
↓
completionJSON调用
- vision: true
- images: [base64字符串数组]
↓
AI成功分析图片内容
↓
返回8维度设计分析
data:image/jpeg;base64,/9j/4AAQSkZJRgABAQEASABIAAD...
data:image/jpeg;base64,Base64编码会增加约33%的大小:
建议:
并行转换(可选):
const base64Images = await Promise.all(
options.images.map(url => urlToBase64(url))
);
缓存机制(可选):
// 缓存已转换的base64,避免重复转换
const base64Cache = new Map<string, string>();
进度反馈:
options.onProgressChange?.(`正在转换图片 ${i + 1}/${total}...`);
优点:
缺点:
优点:
缺点:
🔄 开始将图片URL转换为base64...
📥 下载图片1: https://file-cloud.fmode.cn/.../test.jpg
📦 图片1大小: 3.24MB
✅ 图片1已转换为base64 (4320.56KB)
✅ 所有图片已转换为base64,共1张
🤖 调用豆包1.6模型...
📸 原始图片URL: ["https://..."]
📸 base64图片数量: 1
🚀 开始调用completionJSON进行vision分析...
✅ AI分析完成,原始内容长度: 2341
📝 AI返回内容预览: 一、空间定位与场景属性
这是一个现代法式风格的客餐厅一体化空间...
🔄 开始将图片URL转换为base64...
📥 下载图片1: https://file-cloud.fmode.cn/.../test.jpg
❌ 图片1转换失败: TypeError: Failed to fetch
原因:网络错误或图片URL无法访问
错误信息:图片1无法访问: Failed to fetch
请确保图片URL可公开访问
原因:图片过大或网络慢
解决:
原因:图片URL无法访问
解决:
原因:可能不是URL访问问题
检查:
data:image/...前缀)原因:图片太大,base64字符串过长
解决:
该项目也使用completionJSON和vision: true:
const result = await completionJSON(
prompt,
output,
(content) => {
// 流式回调
},
2,
{
model: 'fmode-1.6-cn',
vision: true,
images: options.images // 直接传URL
}
);
成功原因:
与本项目差异:
在上传时自动压缩大图:
async function compressImage(file: File): Promise<File> {
// 使用Canvas API压缩
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d')!;
const img = await loadImage(file);
// 设置最大尺寸
const maxWidth = 2000;
const maxHeight = 2000;
let width = img.width;
let height = img.height;
if (width > maxWidth || height > maxHeight) {
const ratio = Math.min(maxWidth / width, maxHeight / height);
width *= ratio;
height *= ratio;
}
canvas.width = width;
canvas.height = height;
ctx.drawImage(img, 0, 0, width, height);
return canvasToFile(canvas);
}
避免重复转换:
const base64Cache = new Map<string, string>();
async function getCachedBase64(url: string): Promise<string> {
if (base64Cache.has(url)) {
return base64Cache.get(url)!;
}
const base64 = await urlToBase64(url);
base64Cache.set(url, base64);
return base64;
}
长期方案:配置存储服务允许公开读取
更新日期: 2025-11-27
状态: ✅ 已实现并测试
测试结果: 等待用户反馈