You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何通过Cloudflare Worker从R2获取PDF并在React SPA中正常展示?

问题描述

我正使用Cloudflare官方推出的Vite Cloudflare插件,将React SPA从传统Nginx主机迁移到Cloudflare Worker。该SPA需要从R2存储桶获取图片和PDF文件,目前图片获取功能正常,客户端代码如下:

const getR2Data = (r2Url) => {
  const url = "/api?r2Image=" + r2Url;
  fetch(url)
    .then((res) => res.body)
    .then((rs) => {
      const reader = rs.getReader();
      return new ReadableStream({
        async start(controller) {
          while (true) {
            const { done, value } = await reader.read();
            if (done) {
              break;
            }
            controller.enqueue(value);
          }
          controller.close();
          reader.releaseLock();
        }
      })
    })
    .then(rs => new Response(rs))
    .then(response => response.blob())
    .then(blob => URL.createObjectURL(blob))
    .then(binaryData => setBinaryData(binaryData))
    .catch((err) => {
      console.log("R2 fetch error:")
      console.log(err);
    })
}

在/worker/index.js中使用了稍作修改的默认fetch()代码,setBinaryData()用于设置<img>标签的状态变量。但使用同一函数获取PDF并尝试在<object>标签中展示时(该方式在Nginx环境下正常):

<object
  data={pdfBinary}
  type="application/pdf"
  width="100%"
  height="100%"
>
  <p>text</p>
</object>

却无法正常工作,PDF源码可见且渲染耗时极长。尝试<embed>和<iframe>标签也仅显示PDF源码,推测问题出在R2文件获取方式上。此外,将PDF转为下载功能时可正常运行:

.then( blob => {
  var fileURL = URL.createObjectURL(blob);
  var fileLink = document.createElement('a');
  fileLink.href = fileURL;
  fileLink.download = `whatever.pdf`;
  fileLink.click();
})

困惑于为何与Nginx环境下从/pdfs/data.pdf读取文件的表现不同,请问需如何修改才能让PDF在<object>标签中正常展示?

解决方案

问题核心出在响应的Content-Type头部以及手动处理ReadableStream的冗余逻辑上,以下是具体修复步骤:

1. 简化客户端fetch逻辑,保留原始响应元数据

当前代码手动重新封装ReadableStream时丢失了原始响应的Content-Type等关键头部,而<object>渲染PDF需要明确的application/pdf类型标识。直接使用原始响应转换blob即可:

const getR2Data = (r2Url) => {
  const url = "/api?r2Image=" + r2Url;
  fetch(url)
    .then(response => response.blob())
    .then(blob => {
      // 兜底处理:若后端未返回Content-Type,根据文件名后缀指定
      if (!blob.type && r2Url.endsWith('.pdf')) {
        blob = new Blob([blob], { type: 'application/pdf' });
      }
      return URL.createObjectURL(blob);
    })
    .then(binaryData => setBinaryData(binaryData))
    .catch(err => {
      console.log("R2 fetch error:");
      console.log(err);
    });
};

2. 确保Worker端返回正确的响应头部

在/worker/index.js中,从R2获取文件后必须传递正确的Content-Type,这是浏览器识别文件类型的关键:

// Worker中处理R2请求的核心代码示例
async function handleR2Request(request) {
  const url = new URL(request.url);
  const r2Key = url.searchParams.get('r2Image');
  if (!r2Key) return new Response('Missing key', { status: 400 });

  const object = await R2_BUCKET.get(r2Key);
  if (!object) return new Response('Not found', { status: 404 });

  // 优先使用R2对象自带的元数据,否则根据后缀推断
  const contentType = object.httpMetadata?.contentType || 
                      (r2Key.endsWith('.pdf') ? 'application/pdf' : 'image/jpeg');

  return new Response(object.body, {
    headers: {
      'Content-Type': contentType,
      'Cache-Control': 'public, max-age=31536000'
    }
  });
}

3. 验证<object>标签的属性一致性

确保pdfBinary状态更新正确,且标签的type属性与blob的Content-Type严格匹配:

<object
  data={pdfBinary}
  type="application/pdf"
  width="100%"
  height="100%"
>
  <p>无法加载PDF,请<a href={pdfBinary} target="_blank">点击查看</a></p>
</object>

差异原因说明

Nginx会自动根据文件后缀设置正确的Content-Type响应头,而你之前的Worker代码未正确传递或设置该头部,导致浏览器无法识别PDF的MIME类型,只能以纯文本形式解析展示。

内容的提问来源于stack exchange,提问作者Omortis

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.13 08:48:48