fix: 彻底禁止base64存储 - 所有图片/视频上传均先传服务器获取URL
This commit is contained in:
parent
7fdaa5d9b5
commit
98bc457082
@ -458,16 +458,43 @@ export function ImageNode({ node, isSelected }: ImageNodeProps) {
|
||||
};
|
||||
|
||||
// 图片标注完成回调
|
||||
const handleAnnotateComplete = (annotatedImageUrl: string) => {
|
||||
const handleAnnotateComplete = async (annotatedImageUrl: string) => {
|
||||
setAnnotatorOpen(false);
|
||||
|
||||
// 如果仍然是 base64 格式,先上传到服务器(ImageAnnotator 应该已经处理了,这里作为保护)
|
||||
let finalUrl = annotatedImageUrl;
|
||||
if (annotatedImageUrl.startsWith("data:")) {
|
||||
try {
|
||||
toast.info("正在上传图片...");
|
||||
const response = await fetch(annotatedImageUrl);
|
||||
const blob = await response.blob();
|
||||
const formData = new FormData();
|
||||
formData.append("file", blob, `annotated-${Date.now()}.png`);
|
||||
const uploadResponse = await fetch("/api/files/upload", {
|
||||
method: "POST",
|
||||
body: formData,
|
||||
});
|
||||
if (uploadResponse.ok) {
|
||||
const result = await uploadResponse.json();
|
||||
finalUrl = result.url;
|
||||
} else {
|
||||
toast.error("上传失败");
|
||||
return;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("上传标注图片失败:", error);
|
||||
toast.error("上传失败");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// 更新当前节点,标记为已标注图片
|
||||
// 标注后的图片会带有视觉标记(箭头、圆圈、文字等)
|
||||
// 这些标记指示了用户希望 AI 重点关注/修改的区域
|
||||
updateNode(node.id, {
|
||||
data: {
|
||||
...data,
|
||||
src: annotatedImageUrl,
|
||||
src: finalUrl,
|
||||
hasAnnotations: true, // 🆕 标记为已标注
|
||||
originalSrc: data.originalSrc || data.src, // 保留原始图片
|
||||
prompt: data.prompt ? `${data.prompt} (已标注)` : '已标注图片',
|
||||
|
||||
@ -32,8 +32,8 @@ export function VideoNode({ node, isSelected }: VideoNodeProps) {
|
||||
const updateNode = useCanvasStore((state) => state.updateNode);
|
||||
const deleteNode = useCanvasStore((state) => state.deleteNode);
|
||||
|
||||
// 处理视频上传
|
||||
const handleVideoUpload = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
// 处理视频上传 - 上传到服务器获取 URL
|
||||
const handleVideoUpload = async (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
const file = e.target.files?.[0];
|
||||
if (!file) return;
|
||||
|
||||
@ -43,14 +43,40 @@ export function VideoNode({ node, isSelected }: VideoNodeProps) {
|
||||
return;
|
||||
}
|
||||
|
||||
const reader = new FileReader();
|
||||
reader.onload = (event) => {
|
||||
const src = event.target?.result as string;
|
||||
updateNode(node.id, {
|
||||
data: { ...data, src, status: 'completed' },
|
||||
// 更新状态为上传中
|
||||
updateNode(node.id, {
|
||||
data: { ...data, status: 'generating' },
|
||||
});
|
||||
|
||||
try {
|
||||
// 上传视频到服务器
|
||||
const formData = new FormData();
|
||||
formData.append('file', file);
|
||||
|
||||
const response = await fetch('/api/video/upload', {
|
||||
method: 'POST',
|
||||
body: formData,
|
||||
});
|
||||
};
|
||||
reader.readAsDataURL(file);
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error('上传失败');
|
||||
}
|
||||
|
||||
const result = await response.json();
|
||||
|
||||
// 使用服务器返回的 URL
|
||||
updateNode(node.id, {
|
||||
data: { ...data, src: result.url, status: 'completed' },
|
||||
});
|
||||
|
||||
toast.success('视频上传成功');
|
||||
} catch (error) {
|
||||
console.error('视频上传失败:', error);
|
||||
toast.error('视频上传失败,请重试');
|
||||
updateNode(node.id, {
|
||||
data: { ...data, status: 'failed' },
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// 播放/暂停
|
||||
|
||||
@ -797,13 +797,38 @@ function PureMultimodalInput({
|
||||
});
|
||||
toast.success(`已添加标注:${annotation.description}`);
|
||||
}}
|
||||
onDrawingComplete={(annotatedImageUrl) => {
|
||||
onDrawingComplete={async (annotatedImageUrl) => {
|
||||
// 将标注后的图片替换原图片(或添加为新附件)
|
||||
const fileName = `annotated-${Date.now()}.png`;
|
||||
|
||||
// 如果仍然是 base64 格式,先上传到服务器
|
||||
let finalUrl = annotatedImageUrl;
|
||||
if (annotatedImageUrl.startsWith("data:")) {
|
||||
try {
|
||||
toast.info("正在上传图片...");
|
||||
const response = await fetch(annotatedImageUrl);
|
||||
const blob = await response.blob();
|
||||
const formData = new FormData();
|
||||
formData.append("file", blob, fileName);
|
||||
const uploadResponse = await fetch("/api/files/upload", {
|
||||
method: "POST",
|
||||
body: formData,
|
||||
});
|
||||
if (uploadResponse.ok) {
|
||||
const result = await uploadResponse.json();
|
||||
finalUrl = result.url;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("上传标注图片失败:", error);
|
||||
toast.error("上传失败");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
setAttachments((prev) => [
|
||||
...prev,
|
||||
{
|
||||
url: annotatedImageUrl,
|
||||
url: finalUrl,
|
||||
name: fileName,
|
||||
contentType: "image/png",
|
||||
},
|
||||
|
||||
@ -15,6 +15,7 @@ import {
|
||||
} from "@/components/ui/dropdown-menu";
|
||||
import { ImageAnnotator } from "@/components/image-annotator";
|
||||
import { VideoPlayer } from "@/components/video-player";
|
||||
import { toast } from "sonner";
|
||||
|
||||
type RankingItem = {
|
||||
rank: number;
|
||||
@ -130,10 +131,37 @@ export function TopNavbar({
|
||||
};
|
||||
|
||||
// 处理图片编辑完成
|
||||
const handleImageEditorConfirm = (annotatedImageUrl: string) => {
|
||||
const handleImageEditorConfirm = async (annotatedImageUrl: string) => {
|
||||
// 如果仍然是 base64 格式,先上传到服务器(ImageAnnotator 应该已经处理了,这里作为保护)
|
||||
let finalUrl = annotatedImageUrl;
|
||||
if (annotatedImageUrl.startsWith("data:")) {
|
||||
try {
|
||||
toast.info("正在上传图片...");
|
||||
const response = await fetch(annotatedImageUrl);
|
||||
const blob = await response.blob();
|
||||
const formData = new FormData();
|
||||
formData.append("file", blob, `edited-${Date.now()}.png`);
|
||||
const uploadResponse = await fetch("/api/files/upload", {
|
||||
method: "POST",
|
||||
body: formData,
|
||||
});
|
||||
if (uploadResponse.ok) {
|
||||
const result = await uploadResponse.json();
|
||||
finalUrl = result.url;
|
||||
} else {
|
||||
toast.error("上传失败");
|
||||
return;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("上传图片失败:", error);
|
||||
toast.error("上传失败");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// 将编辑后的图片存储到 sessionStorage,以便在聊天页面中使用
|
||||
try {
|
||||
sessionStorage.setItem("pending-edited-image", annotatedImageUrl);
|
||||
sessionStorage.setItem("pending-edited-image", finalUrl);
|
||||
} catch (error) {
|
||||
// sessionStorage不可用(如隐私模式),使用URL参数作为备选方案
|
||||
console.warn("sessionStorage不可用,尝试使用URL参数:", error);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user