Next.js 13批量传图至Cloudinary并同步URL到Supabase求助
解决Next.js 13批量上传图片到Cloudinary并同步所有URL到Supabase的问题
问题根源
- Cloudinary单条上传请求仅支持处理一个文件,原代码一次性将所有文件放入同一个FormData提交,只会上传最后一个选中的图片
- 未遍历收集所有图片的上传返回URL,仅获取了单个图片地址
- 存入Supabase时仅保存了单个URL,未批量存储所有图片链接
修改后的完整代码
'use client'; import { useState, useRef } from 'react' import { useUser } from '@/contexts/AuthContext'; import { Database } from '@/db_types' import { redirect } from 'next/navigation'; import Button from "@/components/shared/Button" import Editor from "@/components/shared/Editor" type Posts = Database['public']['Tables']['posts']['Row'] const initialState = { title: "", description: "", slug: "", images: [], }; export default function AddPost() { const { user, userDetails , supabase } = useUser(); const [loading, setLoading] = useState(false) const [title, setTitle] = useState<Posts['title']>(null) const [description, setDescription] = useState<Posts['description']>(null) const [slug, setSlug] = useState<Posts['slug']>(null) const [imageData, setImageData] = useState(initialState); const imageInputRef = useRef<HTMLInputElement>(null); if (user) { redirect('/'); } // 单个图片上传函数 const uploadSingleImage = async (file: File) => { const data = new FormData(); data.append("file", file); data.append("upload_preset", "c_tags"); const res = await fetch(`${process.env.NEXT_PUBLIC_CLOUDINARY_API}`, { method: "POST", body: data, }); const result = await res.json(); return result.secure_url; } async function addPost() { try { const files = imageInputRef.current?.files; if (!files || files.length === 0) { alert("请选择至少一张图片"); return; } setLoading(true); // 批量上传所有图片,收集所有URL const imageUrls = await Promise.all( Array.from(files).map(file => uploadSingleImage(file)) ); const updates = { title, description, slug, // 根据你的Supabase字段类型选择:如果是JSON数组就直接存imageUrls,否则用逗号分隔字符串 images: JSON.stringify(imageUrls), // 或者用逗号分隔:images: imageUrls.join(',') created_at: new Date().toISOString(), user_id: userDetails?.id, }; let { error } = await supabase.from("posts").upsert(updates); if (error) throw error; alert("发布成功!"); // 重置表单 setTitle(null); setDescription(null); setSlug(null); setImageData(initialState); if (imageInputRef.current) imageInputRef.current.value = ""; } catch (error) { alert("数据更新失败!"); console.log(error); } finally { setLoading(false); } } return ( <div className=""> <div> <form className="mt-3" onSubmit={(e) => { e.preventDefault(); addPost() }} > <input id="images" type="file" className="relative block w-full appearance-none rounded-none rounded-md border border-gray-300 px-3 py-2 text-gray-900 placeholder-gray-500 focus:z-10 focus:border-indigo-500 focus:outline-none focus:ring-indigo-500 sm:text-sm" accept="image/*" ref={imageInputRef} multiple /> <label htmlFor="title">Title</label> <input id="title" type="text" className="relative block w-full appearance-none rounded-none rounded-md border border-gray-300 px-3 py-2 text-gray-900 placeholder-gray-500 focus:z-10 focus:border-indigo-500 focus:outline-none focus:ring-indigo-500 sm:text-sm" value={title || ''} onChange={(e) => setTitle(e.target.value)} /> <label htmlFor="Description">Content</label> <Editor description={description} setDescription={setDescription} /> <label htmlFor="slug">Slug</label> <input id="slug" type="text" className="relative block w-full appearance-none rounded-none rounded-md border border-gray-300 px-3 py-2 text-gray-900 placeholder-gray-500 focus:z-10 focus:border-indigo-500 focus:outline-none focus:ring-indigo-500 sm:text-sm" value={slug || ''} onChange={(e) => setSlug(e.target.value)} /> <div> <Button className="mt-5 bg-red-500" type="submit" disabled={loading} > {loading ? "发布中..." : "Publish"} </Button> </div> </form> </div> </div> ); }
关键修改说明
- 拆分出
uploadSingleImage函数,负责单个图片的上传逻辑 - 使用
Promise.all批量处理所有选中图片的上传请求,并行提升效率 - 收集所有上传后的图片URL,根据Supabase的
images字段类型选择存储格式(JSON数组或逗号分隔字符串) - 优化表单重置逻辑,上传成功后清空输入状态
- 添加加载状态禁用按钮,防止重复提交
内容的提问来源于stack exchange,提问作者Tan Huynh
相关产品推荐
相关产品推荐

