如何通过Lambda生成的S3预签名URL实现前端直接下载存储桶文件
问题根因
你当前的代码逻辑是直接把API Gateway返回的Lambda响应内容(也就是包裹着预签名URL的JSON文本)封装成Blob触发下载,最终下载到的自然是存着URL字符串的JSON文件,根本没有访问S3上的实际目标文件。
修改方案
核心调整逻辑是先解析接口返回内容拿到真实的S3预签名URL,再基于这个URL触发目标文件下载,有两种可直接复用的实现方式:
- 方式一:直接指向预签名URL触发下载(代码最简,要求S3存储桶已配置当前站点的跨域访问规则)
- 方式二:二次请求预签名URL拿到文件流后转Blob触发下载(兼容性最好,可强制触发下载避免浏览器预览文件,无跨域问题)
可直接替换的代码
方式一(最简实现)
fetch("https://********.execute-api.us-east-1.amazonaws.com/test", requestOptions) .then(res => res.json()) .then(presignedUrl => { // 去除接口返回可能包裹URL的多余引号 const s3FileUrl = presignedUrl.replace(/^"|"$/g, '') const downloadLink = document.createElement("a") downloadLink.href = s3FileUrl // 替换为你需要的实际保存文件名,要带上对应后缀,比如demo.pdf、resource.zip downloadLink.download = "实际目标文件名" downloadLink.style.display = "none" document.body.appendChild(downloadLink) downloadLink.click() document.body.removeChild(downloadLink) })
方式二(兼容最优实现)
如果方式一出现跨域报错,或者点击后文件被浏览器直接打开预览而非下载,用这个版本:
fetch("https://********.execute-api.us-east-1.amazonaws.com/test", requestOptions) .then(res => res.json()) .then(async presignedUrl => { const s3FileUrl = presignedUrl.replace(/^"|"$/g, '') // 请求S3拿到真实文件流 const fileResponse = await fetch(s3FileUrl) const fileBlob = await fileResponse.blob() const downloadLink = document.createElement("a") const objectUrl = window.URL.createObjectURL(fileBlob) downloadLink.href = objectUrl downloadLink.download = "实际目标文件名" downloadLink.style.display = "none" document.body.appendChild(downloadLink) downloadLink.click() // 释放内存,移除临时节点 window.URL.revokeObjectURL(objectUrl) document.body.removeChild(downloadLink) })
注意事项
a.download属性的文件名必须和目标文件格式匹配,否则下载后的文件会出现格式错误无法打开的问题- 如果Lambda返回的预签名URL本身已经在S3签名里指定了
response-content-disposition=attachment响应头,方式一也会强制触发下载,不会出现预览问题
内容的提问来源于stack exchange,提问作者user14677532
相关产品推荐
相关产品推荐

