使用nodejs做文件下载中转

1,918 阅读3分钟
原文链接: www.oecom.cn

之前做了一个功能就是点击按钮实现文件下载,文件保存在了阿里云的OSS上,阿里的OSS和七牛的OSS其实个人感觉差不多,一般情况下,前端下载文件很多都是通过一个a标签来进行下载。但是对于OSS存储的文件比如图片点击后在浏览器直接打开了,即使是添加了download属性也无济于事,于是我就想到了使用nodejs来搭建一个中转站。

我们先来直接看一下示例代码,然后再进行说明:

router.get('/downloadFile',function(req,res){
    try{
        let filePath = req.query.filePath;
        let fileName = req.query.fileName;
        let imgProtocol = filePath.slice(0,filePath.indexOf(":"));
        let imgName = fileName||filePath.slice(filePath.lastIndexOf("/")+1);
        if(imgProtocol==='https'){
            https.get(filePath,function(imgReq,imgRes){  //path为网络文件地址
                var fileSize = imgReq.headers["content-length"];
                imgReq.setEncoding('binary');
                res.setHeader("Content-Type", "application/octet-stream");
                res.setHeader("content-length",fileSize);
                res.setHeader("Content-Disposition", "attachment; filename="+encodeURI(imgName));
                res.setTimeout(30*60*1000)
                imgReq.on('data',function(chunk){
                    res.write(chunk,"binary"); //格式必须为 binary,否则会出错
                })
                imgReq.on('end',function(){
                    console.log("文件下载完毕");
                    res.end()
                })
            })
        }else{
            http.get(filePath,function(imgReq,imgRes){  //path为网络文件地址
                var fileSize = imgReq.headers["content-length"];
                imgReq.setEncoding('binary');
                res.setHeader("Content-Type", "application/octet-stream");
                res.setHeader("content-length",fileSize);
                res.setHeader("Content-Disposition", "attachment; filename="+encodeURI(imgName));
                res.setTimeout(30*60*1000)
                imgReq.on('data',function(chunk){
                    res.write(chunk,"binary"); //格式必须为 binary,否则会出错
                })
                imgReq.on('end',function(){
                    console.log("文件下载完毕");
                    res.end()
                })
            })
        }
    }catch(e){
        res.end(JSON.stringify({success: false, msg: e.stack}));
    }

})

从上面的代码中我们可以看到,接口接收的参数有两个,一个是文件名,一个是文件路径,如果没有文件名我们就默认文件路径的最后一个斜杠后面就是文件名了。接下来要做的就是区分文件存储的环境是https还是http,两种方式没有什么别的区别,只是请求的模块不一样,当然这里我们也可以使用request模块,不在多说。当get请求有响应后,我们开始做向客户端返回数据的准备。

如上面代码中所示,我们获取了content-length,来告诉客户端浏览器将要下载的文件总大小是多少。然后设置文件的编码为binary,也就是二进制的格式,为什么要设置成二进制呢?是因为我们不知道即将下载的文件格式具体是什么。所以我们也将Content-Type的内容设置成了application/octet-stream。随后设置的就是文件大小和Content-Disposition,在这里我们对filename进行了url转码,是因为如果直接使用中文,在这里会报错的。

再后面我们设置了一个超时时间为30分钟,因为nodejs默认的接口超时时间为2分钟,这对于下载一些大文件来说很不现实。我设置30分钟是因为这里文件的大小不超过200M,30分钟足矣下载完成,当然,你也可以设置为setTimeout(0),使其超时时间不做限制。

随后当请求返回数据后,我们也将数据写入到接口的响应体中,同时编码格式也是二进制。直到流获取完成,此时也将数据全部都写入到了响应体中,之后调用res.end来结束连接。

如此,一个使用nodejs来作为文件下载中转的例子就写好了。