
本文详解如何在 react 应用中不依赖 cloudinary 前端 widget 或 jquery sdk,直接通过原生 javascript 调用 cloudinary rest api 实现大文件(>100mb)的无后端中转、分片上传,支持 unsigned 与 signed 两种安全模式。
Cloudinary 官方 Node.js 后端 SDK(如 cloudinary.v2.uploader.upload_stream 和 upload_large_stream)虽内置完善的流式分片逻辑,但因其强依赖 Node.js 运行时(如 fs, stream, Buffer),无法在浏览器环境中运行。因此,前端需绕过 SDK,采用标准 HTTP 请求 + 分片协议,直接对接 Cloudinary 的 Upload API。
✅ 核心方案:纯前端分片上传(Chunked Upload)
Cloudinary 原生支持客户端分片上传(Chunked Upload),无需任何 SDK。其流程如下:
-
初始化上传会话:向 https://api.cloudinary.com/v1_1/
/auto/upload 发起 POST,携带 upload_preset(unsigned)或签名参数(signed),获取 upload_id; -
分片上传:将文件按 chunk_size(推荐 5–20 MB)切片,逐个调用 PUT /v1_1/
/auto/upload/ ,附带 Content-Range 头; - 完成上传:发送最终 POST 请求确认上传完成,返回资源 URL。
以下为 React 中的精简实现示例(使用 fetch + AbortController):
// utils/cloudinaryUpload.ts
export const uploadLargeFile = async (
file: File,
cloudName: string,
uploadPreset: string,
signatureEndpoint?: string // 可选:后端签名接口
): Promise<{ secure_url: string; public_id: string }> => {
const chunkSize = 10 * 1024 * 1024; // 10MB/chunk
const totalChunks = Math.ceil(file.size / chunkSize);
let uploadId: string;
// Step 1: 初始化上传(unsigned 或 signed)
const initPayload = { upload_preset: uploadPreset };
if (signatureEndpoint) {
const { timestamp, signature } = await fetch(signatureEndpoint, {
method: 'POST',
body: JSON.stringify({ file_size: file.size, file_type: file.type }),
}).then(r => r.json());
Object.assign(initPayload, { timestamp, signature, api_key: 'YOUR_API_KEY' });
}
const initRes = await fetch(
`https://api.cloudinary.com/v1_1/${cloudName}/auto/upload`,
{ method: 'POST', body: JSON.stringify(initPayload) }
);
const initJson = await initRes.json();
uploadId = initJson.upload_id;
// Step 2: 分片上传
for (let i = 0; i < totalChunks; i++) {
const start = i * chunkSize;
const end = Math.min(start + chunkSize, file.size);
const chunk = file.slice(start, end);
const blob = new Blob([chunk], { type: file.type });
await fetch(
`https://api.cloudinary.com/v1_1/${cloudName}/auto/upload/${uploadId}`,
{
method: 'PUT',
headers: {
'Content-Range': `bytes ${start}-${end - 1}/${file.size}`,
'Content-Type': file.type,
},
body: blob,
}
);
}
// Step 3: 完成上传
const completeRes = await fetch(
`https://api.cloudinary.com/v1_1/${cloudName}/auto/upload/${uploadId}`,
{ method: 'POST' }
);
return completeRes.json();
};
// 在组件中调用
function UploadButton() {
const handleUpload = async () => {
const file = document.querySelector('input[type="file"]')?.files?.[0];
if (!file) return;
try {
const result = await uploadLargeFile(
file,
'your_cloud_name',
'your_upload_preset',
'/api/cloudinary/sign' // 若启用 signed 上传
);
console.log('Upload success:', result.secure_url);
} catch (err) {
console.error('Upload failed:', err);
}
};
return (
<>
>
);
}⚠️ 关键注意事项
- Unsigned 上传限制:仅允许预设白名单中的参数(如 folder, tags, context),且禁止设置 public_id 或 overwrite;适合公开、低敏感场景。
-
Signed 上传更安全:需后端生成签名(基于 api_secret + 参数排序 + SHA1),可完全控制上传行为(如强制 public_id、resource_type、eager 转码等)。签名逻辑示例(Node.js):
// POST /api/cloudinary/sign app.post('/sign', (req, res) => { const { file_size, file_type, ...rest } = req.body; const timestamp = Math.floor(Date.now() / 1000); const params = { timestamp, file_size, file_type, ...rest }; const sorted = Object.keys(params) .sort() .map(k => `${k}=${params[k]}`) .join('&'); const signature = crypto .createHash('sha1') .update(sorted + process.env.CLOUDINARY_API_SECRET) .digest('hex'); res.json({ timestamp, signature, api_key: process.env.CLOUDINARY_API_KEY }); }); - 错误重试与断点续传:生产环境应添加 fetch 重试机制、AbortController 中断支持,并持久化 upload_id 与已上传分片索引,实现断点续传。
- CORS 配置:确保 Cloudinary 域名(api.cloudinary.com)已在 Cloudinary 控制台的 Settings → Security → CORS 中正确配置允许的前端域名。
✅ 总结
放弃“复用后端 SDK”的思路,转而采用 Cloudinary 原生 Chunked Upload API,是 React 等现代前端框架实现大文件直传的最佳实践。它轻量、可控、符合安全规范,且完全规避了服务端带宽与内存瓶颈。结合 signed 上传与后端签名服务,即可在保障灵活性的同时满足企业级安全审计要求。
立即学习“前端免费学习笔记(深入)”;










