如何在前端(React)中实现 Cloudinary 大文件分片直传

本文详解如何在 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。其流程如下:

  1. 初始化上传会话:向 https://api.cloudinary.com/v1_1//auto/upload 发起 POST,携带 upload_preset(unsigned)或签名参数(signed),获取 upload_id;
  2. 分片上传:将文件按 chunk_size(推荐 5–20 MB)切片,逐个调用 PUT /v1_1//auto/upload/,附带 Content-Range 头;
  3. 完成上传:发送最终 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 pa

    rams = { 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 上传与后端签名服务,即可在保障灵活性的同时满足企业级安全审计要求。