使用 Cloudfront、nodejs 和 react 从 s3 下载视频
我从未意识到单击按钮下载文件会如此令人头疼,但事实就是如此。经过反复尝试,我做了我们作为开发人员最擅长的事情 — 寻找解决方案。
情况如下:
我使用 S3 和 CloudFront 传输视频文件。视频在后端进行了加密和访问限制,以确保没有人可以直接从我的 S3 存储桶中获取它们。用户可以观看和下载视频。
尝试 1:基本下载逻辑
最初,我尝试了典型的方法:
const handleDownload = () => { const link = document.createElement("a"); link.href = videoUrl; link.download = `${title || "video"}-thook.mp4`; // Provide a default filename if `title` is unavailable. document.body.appendChild(link); link.click(); document.body.removeChild(link); // Cleanup after the download is triggered };
这招管用…… 有点。它没有下载,而是重定向到另一个页面并开始播放视频。
罪魁祸首?我在 S3 上传过程中设置了“ContentType”。
const param = { Bucket: process.env.AWS_S3_BUCKET_NAME, Key: `${directory}/${uuid()}-${file.originalname}`, Body: file.buffer, // ContentType: file.mimetype ❌ (Removed this) }; await s3.upload(param).promise();
删除“ContentType”设置后,文件终于开始在桌面上正确下载。
尝试 2:移动版 Safari 和其他浏览器问题
下一个问题是什么?移动设备上的 Safari 和其他一些浏览器仍然坚持打开视频而不是下载它。
为了解决这个问题,我决定使用 Node.js 从后端传输视频并设置适当的标头来强制下载:
import axios from "axios"; const downloadWithSignedURL = async (req: Request, res: Response) => { const signedUrl = req.query.signedUrl as string; const filename = (req.query.filename as string) || "video.mp4"; if (!signedUrl) { return res.status(400).send("Signed URL is required."); } try { // Fetch the video stream using axios const response = await axios.get(signedUrl, { responseType: "stream", }); if (response.status === 200) { // Set headers to force download res.setHeader("Content-Disposition", `attachment; filename="${filename}-THOOK.mp4"`); res.setHeader("Content-Type", "video/mp4"); // Pipe the video stream to the client response.data.pipe(res); } else { res.status(response.status || 500).send("Failed to fetch video."); } } catch (error) { console.error("Error handling download:", error); res.status(500).send("Failed to handle download."); } };
//route.ts ... router.get("/downloadVideo", DownloadController.downloadWithSignedURL);
在前端,我调整了逻辑以与此后端路由进行交互:
//in react const handleDownload = () => { const downloadUrl = `${API_ROUTE}/downloadVideo?signedUrl=${encodeURIComponent(url)}&filename=${encodeURIComponent(name || "video.mp4")}`; window.open(downloadUrl, "_blank"); };
最终解决方案:无缝下载,无需新标签
为了提高用户体验,我修改了逻辑,无需打开新选项卡即可直接触发下载:
const handleDownload = () => { if (!url) { alert("Please provide the signed URL."); return; } try { const downloadUrl = `${API_ROUTE}/downloadVideo?signedUrl=${encodeURIComponent(url)}&filename=${encodeURIComponent(name || "video.mp4")}`; const link = document.createElement("a"); link.href = downloadUrl; link.setAttribute("download", name || "video.mp4"); document.body.appendChild(link); link.click(); link.remove(); } catch (error) { console.error("Download error:", error); } };
瞧!点击按钮后,下载立即开始,不会中断。
这就是我创建下载功能的方法:
附注
以下是该过程中的一些关键见解:
感谢阅读🙇♂️。