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

React应用通过S3预签名URL下载文件遇CORS问题求助

S3预签名URL下载音频触发CORS错误问题

我有一个部署在S3(搭配CloudFront与自定义域名)的React应用,对接Python Django后端。前端向后端请求.wav音频文件的S3预签名下载URL,音频播放器可正常加载播放音频,但点击下载按钮时触发CORS错误:

.... has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.

此前将预签名URL直接作为<a>标签的href可在新标签页打开文件,但我需要让用户将文件以example.wav格式下载到本地,因此需通过React应用发起请求获取文件数据。该功能在本地环境正常,部署后失效。

我知道问题源于S3未返回Access-Control-Allow-Origin响应头,但S3的CORS配置看似正确,且已尝试多种Axios请求头配置。虽看到过CloudFront会剥离请求头的相关问题,但我的React应用是直接与S3通信,理论上不受影响。

以下是关键代码片段(假设awsFileLink为S3中.wav文件的预签名下载URL):

音频播放器代码

...
<audio
  onEnded={() => setIsPlaying(!isPlaying)}
  preload="metadata"
  src={awsFileLink}
  ref={audioPlayer}
  onLoadedMetadata={onLoadedMetadata}
>
  <track kind="captions" />
</audio>
...

音频下载代码

const downloadFile = async (downloadUrl: string): Promise<any> => {
  try {
    return await axios.get(downloadUrl, { responseType: 'blob' });
  } catch (e) {
    console.log('Error', e);
    return e;
  }
};

const useDownloadAudioFile = (awsFileLink: string, fileExtension: string) => {
  const fetchAudioFile = useCallback(async () => downloadFile(awsFileLink), [awsFileLink]);

  const onDownloadHandler = useCallback(async () => {
    try {
      const { data } = await fetchAudioFile();

      const url = window.URL.createObjectURL(data);

      const link = document.createElement('a');

      link.href = url;
      link.setAttribute(
        'download',
        `${uuidv4()}.${fileExtension}`,
      );

      // Append to html link element page
      document.body.appendChild(link);

      // Start download
      link.click();

      // Clean up and remove the link
      link.parentNode?.removeChild(link);
    } catch (e) {
      console.log('Error', e);
    }
  }, [fetchAudioFile]);

  return onDownloadHandler;
};

Amazon S3 CORS配置(功能正常后将缩小范围)

[
    {
        "AllowedHeaders": [
            "*"
        ],
        "AllowedMethods": [
            "GET",
            "PUT",
            "POST",
            "HEAD",
            "DELETE"
        ],
        "AllowedOrigins": [
            "*"
        ],
        "ExposeHeaders": [],
        "MaxAgeSeconds": 3000
    }
]

后端逻辑代码

class S3FileSystem:
    def __init__(self):
        self.client = boto3.client(
            's3',
            settings.AWS_REGION_NAME,
            aws_access_key_id=settings.AWS_ACCESS_KEY_ID,
            aws_secret_access_key=settings.AWS_SECRET_ACCESS_KEY
        )


    def get_presigned_url_for_download(self, key, time=3600):
        object_exists = True
        try:
            object_exists_response =  self.client.get_object_attributes(
                    Bucket= settings.AUDIO_FILES_BUCKET_NAME,
                    Key=key,
                    ObjectAttributes =['ObjectSize']
            )
            if object_exists_response is None or object_exists_response["ObjectSize"] == 0:
                object_exists = False
        except Exception:
            object_exists = False

        return object_exists, self.client.generate_presigned_url(
            ClientMethod='get_object', 
            ExpiresIn=time,
            Params={'Bucket': settings.AWS_ASSETS_BUCKET_NAME, 'Key': key},
            HttpMethod='GET',
        )

我已尝试多种S3 CORS配置、用Fetch替代Axios、添加多种请求头,但均未解决问题。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.24 22:12:46