「微博客」浏览器资源下载相关问题

发布于 2023-06-22  83 次阅读


问题一:访问一个资源的URL后浏览器的行为是什么

常见的有两种行为:

  • 1.在当前窗口/新标签页打开该资源(在线预览)
  • 2.触发浏览器的下载机制,对资源进行下载

那是由什么来决定浏览器采用哪种行为呢?

这里,我们就要了解两个HTTP响应标头了。

  • Content-Type实体头部用于指示资源的MIME类型,在响应中,Content-Type标头告诉客户端实际返回的内容的类型,指示客户端如何显示附加的文件。
  • Content-Disposition是 MIME 协议的扩展,指示回复的内容该以何种形式展示,是以内联的形式(即网页或者页面的一部分,这种方式就与Content-Type指示显示相同),还是以附件的形式下载并保存到本地。

浏览器究竟采用哪种行为,主要是根据请求目标资源URL后服务端返回的响应头来决定的:

如果不设置Content-Disposition,或其值为inline,则会在当前窗口/新标签页打开该资源并进行在线预览。

如果设置了Content-Disposition: attachment; filename="filename.jpg",则会触发“下载”,在浏览器下载器或第三方下载器中进行文件的下载。

以下是一个Node.js作为资源下载服务端的代码示例:

const http = require('http');
const fs = require('fs');

const server = http.createServer((req, res) => {
    const filePath = '/Users/baizihan/.../P1 - 后拉撤回.mp4'; // 替换为要下载的文件路径
    const fileName = 'P1 - 后拉撤回.mp4'; // 替换为要下载的文件名

    // 检查文件是否存在
    fs.access(filePath, fs.constants.F_OK, (err) => {
        if (err) {
            res.statusCode = 404;
            res.end('File not found');
            return;
        }

        // 设置响应头信息
        res.setHeader('Content-Type', 'video/mp4');
        res.setHeader('Content-Disposition', `filename="${encodeURIComponent(fileName)}"`);

        // 创建可读流
        const fileStream = fs.createReadStream(filePath);

        // 将文件流通过管道写入响应流
        fileStream.pipe(res);
    });
});

const port = 3001; // 设置端口号
server.listen(port, () => {
    console.log(`Server is running on port ${port}`);
});

另外,经过测试,只要后端设置了Content-Disposition: attachment;那么前端的<a></a>标签中设不设置download属性,都会触发下载行为,而后端不设置的话,前端的a标签即使设置download属性,也不一定能触发下载行为。

还有就是有些浏览器,比如移动端Safari会有安全机制,会禁止直接下载跨域资源,会强制在新标签页中打开进行在线预览。另外全平台的Safari用上述方法在线预览视频资源会失败,原因暂时未知。

问题二:如果不修改后端,前端如何进行资源的下载?

如果无法触发浏览器的下载机制的话,那就要靠前端js获取目标资源然后生成Blob URL再让浏览器从内存保存到本地了。

相关操作可阅读这篇博客:「JavaScript」前端资源操作相关小结

当然,这里面也有一些问题。

比如在移动端Safari(~没错又是我~)中,同时触发多个分片下载的Promise后,当前一个文件通过前端下载完成并准备从内存保存到本地之际,浏览器会弹窗提示「你要下载“XXX”吗?」,如下图所示

IMG_9372.jpg

然后便会使其他还在下载中的Promise抛出错误。而其他浏览器一切正常,均不会有此表现。(~万恶的Safari~)

这也就意味着,在移动端Safari中无法进行多任务的并发下载,一次只能进行一个资源的下载……目前未找到比较好的解决方法T_T


A Student on the way to full stack of Web3.