当前位置: 首页 > Web前端 > JavaScript

前端下载图片的N种方法

时间:2023-03-26 21:10:24 JavaScript

前几天,一个简单的图片下载需求折腾了后端大佬好几天,最后还是需要前端来做。借此机会总结一下下载图片有多少种方式。首先启动一个服务使用expressjs启动一个简单的后端服务,首先安装:mkdirdemocddemonpminitnpminstallexpress--save//v4.17.1然后创建一个app.js文件,输入:constexpress=require('express')constapp=express()app.get('/',(req,res)=>{res.send('helloworld')})app.listen(3000,()=>{console.log('服务启动完成')})然后在命令行输入:nodeapp.js,访问http://localhost:3000/,页面显示helloworld,表示服务启动成功。接下来模拟几种情况:情况1.创建一个静态图片的公用文件夹,复制一张随机图片如test.jpg进去,添加如下代码://...app.use(express.static('./public'))//app.listen...浏览器访问http://localhost:3000/test.jpg可以看到图片。案例2.读取图像文件并将其作为流返回app.get('/getFileStream',(req,res)=>{constfileName=req.query.nameconststream=fs.createReadStream(path.resolve('./public/'+fileName))stream.pipe(res)})浏览器访问http://localhost:3000/getFileStream?name=test.jpg即可访问图片。Case3.读取图片文件返回流并添加Content-Disposition响应头。Content-Disposition响应头是MIME协议的扩展。它用于告诉浏览器如何处理服务器发送的文件。有三个值:Content-Disposition:inline//如果浏览器可以直接打开文件,则直接打开,否则触发保存Content-Disposition:attachment//告诉浏览器作为附件发送,它会直接触发保存,接口名会作为默认文件名Content-Disposition:attachment;filename="xxx.jpg"//告诉浏览器以附件形式发送,会直接触发保存,filename的值作为默认文件名app.get('/getAttachmentFileStream',(req,res)=>{constfileName=req.query.name//附件方法实际上设置了两个响应头的值:/*Content-Disposition:attachment;filename="【文件名】"Content-Type:【文件MIME类型】*/res.attachment(fileName);conststream=fs.createReadStream(path.resolve('./public/'+fileName))stream.pipe(res)})情况4.动态生成图片返回流生成两个二维码为例,使用qr-image库创建二维码,添加如下代码:constqr=require('qr-image')app.get('/createQrCode',(req,res)=>{//生成二维码只读流constdata=qr.image(req.query.text,{type:'png'});data.pipe(res)})Case5.返回base64字符串app.get('/createBase64QrCode',(req,res)=>{constdata=qr.image(req.query.text,{type:'png'});constchunks=[]letsize=0data.on('data',(chunk)=>{chunks.push(chunk)size+=chunk.length})data.on('end',()=>{constdata=Buffer.concat(chunks,size)constbase64=`data:image/png;base64,`+data.toString('base64')res.send(base64)})})情况六、post请求方式以上几种情况//解析json类型的请求体app.use(express.json())//解析urlencoded类型的请求体app.use(express.urlencoded())app.post('/getFileStream',(req,res)=>{constfileName=req.body.nameconststream=fs.createReadStream(path.resolve('./public/'+fileName))stream.pipe(res)})app.post('/getAttachmentFileStream',(req,res)=>{constfileName=req.body.nameres.attachment(fileName);conststream=fs.createReadStream(path.resolve('./public/'+fileName))流.pipe(res)})app.post('/createQrCode',(req,res)=>{constdata=qr.image(req.body.text,{type:'png'});data.pipe(res)})a.a标签downloada标签html5版本增加了一个download属性,用于告诉浏览器下载url而不是导航到它,可以有一个属性值,作为文件保存文件时的名称。虽然说有同源限制,但我实际测试,非同源也可以下载对于没有设置Content-Disposition响应头或者设置为inline的图片,因为图片是浏览器可以打开的文件,所以不会触发下载,而是直接打开,浏览器无法预览文件不管是否有Content-Disposition头都会触发保存:jpg静态资源压缩静态资源第三方exe静态资源二维码流jpg流压缩流附件jpg流Attachmentzipstream所以如果要使用a标签下载图片,那么需要在后台添加一个Content-Disposition响应头,并且在流的形式。跨域图片符合这个请求也可以下载,即使响应没有允许跨域的header,但是静态图片即使加了这个header也是直接打开的://经过测试,浏览器还是直接打开图片app.use(express.static('./public',{setHeaders(res){res.attachment()}}))和一个标签也可以使用location.href:location.href='/test.jpg'location.href='/test.zip'行为和标签这两种方法的缺点也很明显。一是不支持post等其他方式的请求,二是需要后端支持。2.base64格式下载a标签支持数据的URL:协议。使用这个,后端可以返回一个base64格式的字符串,然后使用download属性进行下载:这个方法只是一个get或者post请求。缺点是base64字符串可能会很大。传输速度慢,浪费流量。另外,当然还需要后端支持,需要同域或者允许跨域。3.blob格式下载还是tag,也支持blob:protocolURL,用这个设置response类型为blob,然后像base64一样丢给a标签:这个方法需要和上面一样和需要通过ajax请求的一样,需要后台可控,即图片同域或者支持跨域。4.使用canvas下载这个方法其实和方法2、方法3类似,但是相当于改变了图片请求方式:img标签可以跨域,但是跨域的图片绘制到canvas后无法导出,浏览器会报错。可以给img加上crossOrigin属性,但是,如果图片不允许跨域headers,就没用了5、form形式下载post请求下载图片,除了使用上面的方法2和方法3外,还可以使用form形式:使用该方法,图片流的响应头需要设置Content-Disposition,否则浏览器会直接打开图片并且有这个responseHeader,跨域图片也可以下载,即使图片不允许跨域。6、ifrmae下载document.execCommand有一个SaveAs命令,可以触发浏览器的另存为行为。使用这个,你可以加载图片到iframe中,然后通过iframe文档触发命令:图片必须同源,这样理解就可以了,因为只有IE支持。小结本文简单分析一下前端下载图片的各种方法。您可以根据自己的实际需要进行选择。除了最后一种方法,其余方法没有在IE上测试过。如果需要,您可以自己测试。演示代码在https://github.com/wanglin2/download-image-demo。