使用 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);
}
};瞧!点击按钮后,下载立即开始,不会中断。
这就是我创建下载功能的方法:
附注
以下是该过程中的一些关键见解:
感谢阅读🙇♂️。