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
相关产品推荐
相关产品推荐

