NextJS集成Cloudinary指定folder签名时触发Unauthorized错误
我在使用NextJS集成Cloudinary服务时发现,当在签名生成文件sign.js中指定folder参数后,上传接口返回“Unauthorized”错误,但不指定folder参数时可以正常工作。
原有代码
sign.js - 签名生成文件
const cloudinary = require("cloudinary").v2; const sign = async (req, res) => { const timestamp = Math.round((new Date()).getTime() / 1000); const signature = cloudinary.utils.api_sign_request({ timestamp: timestamp, folder: 'product' }, process.env.NEXT_PUBLIC_CLOUDINARY_SECRET); res.statusCode = 200; res.json({ signature, timestamp }); }; export default sign;
index.js - 主业务文件
import Head from 'next/head' import Image from 'next/image' import styles from '../styles/Home.module.css' import { useState } from 'react'; export default function Home() { const [selectedFile, setSelectedFile] = useState(); const [isFilePicked, setIsFilePicked] = useState(false); const [file, setFile] = useState(); const changeHandler = (event) => { console.log(event.target.files[0]); setSelectedFile(event.target.files[0]); setFile(URL.createObjectURL(event.target.files[0])) setIsFilePicked(true); }; const handleSubmission = async () => { const { signature, timestamp } = await getSignature(); const formData = new FormData(); const url = `https://api.cloudinary.com/v1_1/${process.env.NEXT_PUBLIC_CLOUDINARY_CLOUD_NAME}/upload` formData.append('file', selectedFile) formData.append("signature", signature); formData.append("timestamp", timestamp); formData.append("api_key", process.env.NEXT_PUBLIC_CLOUDINARY_KEY); fetch(url, { method: 'POST', body: formData }).then((response) => response.json()) .then((result) => { console.log(result); }) .catch((error) => { console.error('Error:', error); }); } return ( <div className={styles.container}> <Head> <title>Tailwind-Next</title> <meta name="description" content="Generated by create next app" /> <link rel="icon" href="/favicon.ico" /> </Head> <div className={styles.main}> <div className="flex items-center justify-center w-[50%] mb-5"> {selectedFile == null ? <label htmlFor="dropzone-file" className={`flex flex-col items-center justify-center w-full h-64 border-2 border-gray-300 border-dashed rounded-lg cursor-pointer bg-gray-50 dark:hover:bg-bray-800 hover:bg-gray-100 ${styles.dropzone}`}> <div className="flex flex-col items-center justify-center pt-5 pb-6"> <svg aria-hidden="true" className="w-10 h-10 mb-3 text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M7 16a4 4 0 01-.88-7.903A5 5 0 1115.9 6L16 6a5 5 0 011 9.9M15 13l-3-3m0 0l-3 3m3-3v12"></path></svg> <p className="mb-2 text-sm text-gray-500 dark:text-gray-400"><span className="font-semibold">Click to upload</span> or drag and drop</p> <p className="text-xs text-gray-500 dark:text-gray-400">SVG, PNG, JPG or GIF (MAX. 800x400px)</p> </div> <input id="dropzone-file" type="file" className="hidden" accept="image/*" onChange={changeHandler} /> </label> : <div className='flex flex-col items-center justify-center w-full h-64 relative'> <div className={styles.image_wrapper}> <img src={file} className={styles.img_preview} alt="" /> </div> </div>} </div> <button className="w-[50%] bg-blue-600 hover:bg-blue-500 text-white font-bold py-2 px-4 border-b-4 border-blue-700 hover:border-blue-600 rounded" onClick={handleSubmission}> Upload </button> </div> </div> ) } async function getSignature() { const response = await fetch("/api/sign"); const data = await response.json(); const { signature, timestamp } = data; return { signature, timestamp }; }
解决方案
问题出在签名参数与上传请求参数不匹配:Cloudinary的签名验证要求,所有参与签名生成的参数(除api_key、file等特殊参数外),必须同时出现在上传请求的FormData中,且值完全一致。你在签名时加入了folder: 'product',但上传请求里没有携带这个参数,导致签名验证失败,返回Unauthorized。
只需要修改index.js中的handleSubmission函数,在FormData中添加folder参数即可:
const handleSubmission = async () => { const { signature, timestamp } = await getSignature(); const formData = new FormData(); const url = `https://api.cloudinary.com/v1_1/${process.env.NEXT_PUBLIC_CLOUDINARY_CLOUD_NAME}/upload` formData.append('file', selectedFile) formData.append("signature", signature); formData.append("timestamp", timestamp); formData.append("api_key", process.env.NEXT_PUBLIC_CLOUDINARY_KEY); // 新增:添加与签名一致的folder参数 formData.append("folder", "product"); fetch(url, { method: 'POST', body: formData }).then((response) => response.json()) .then((result) => { console.log(result); }) .catch((error) => { console.error('Error:', error); }); }
这样签名参数和上传请求参数完全匹配,验证就能通过,文件会正常上传到指定的product文件夹中。
内容的提问来源于stack exchange,提问作者Harshbardhan Singh
相关产品推荐
相关产品推荐

