原生js解决远程图片无法下载的场景

问题描述:

        当图片url是同源时(没有跨域),要实现下载功能,在a标签加入download属性即可

<a href='图片url' download='图片名称【可加可不加】'></a>

当图片url与前端应用服务非同源时(跨域请求),使用上面的方案就会出现:没有下载,打开了一个类似预览功能的窗口。

在网上查询资料,尝试了以下几种方案

目录

方案1:利用canvas重画图片,转换请求格式

方案2:重新写一个后台接口,并加注解@CrossOrigin

方案3:加入权限验证,转换blod格式,再利用canvas进行下载


方案1:利用canvas重画图片,转换请求格式

<button class='download'>点击下载</button>
$(".download").click(function(){
    //获取图片url和名称,可能是在img标签的src
    let pictureUrl = "";
    let name = "";
    downloadIamge(pictureUrl, name);
})


function downloadIamge(pictureUrl, name) {
            var image = new Image()
            // 解决跨域 Canvas 污染问题
            image.setAttribute('crossOrigin', 'anonymous')
            image.onload = function () {
                var canvas = document.createElement('canvas')
                canvas.width = image.width
                canvas.height = image.height
                var context = canvas.getContext('2d')
                context.drawImage(image, 0, 0, image.width, image.height)
                var url = canvas.toDataURL('image/png')
                // 生成一个a元素,原理是仿造a标签点击事件,实现download功能
                var a = document.createElement('a')
                // 创建一个单击事件
                var event = new MouseEvent('click')
                a.download = name || '下载图片名称'
                // 将生成的URL设置为a.href属性
                a.href = url
                // 触发a的单击事件
                a.dispatchEvent(event);
            }
            //触发onload事件
            image.src = pictureUrl;
        }

         结果反馈:方案失败,因为图片url是来着远程图片服务器上,不能够相应跨域信息 image.setAttribute('crossOrigin', 'anonymous'),控制台报错:

has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled 

方案2:重新写一个后台接口,并加注解@CrossOrigin

        既然方案1失败的原因是后台没有响应前端的跨域请求,那么我想重新写一个后台接口,来响应它不就行了吗,于是看代码:

    @ApiOperation(value = "下载图片", notes = "下载图片")
    @GetMapping(value = "/downloadPicture")
    @CrossOrigin(methods = {RequestMethod.GET})
    public void downloadPicture(String url,String name, HttpServletResponse response) {
        log.info("图片开始下载");
        if(StringUtils.isBlank(url)){
            return;
        }
        response.setCharacterEncoding("UTF-8");
        response.setContentType("image/png;charset=utf-8");
        String group = url.substring(0,url.indexOf("/"));
        String filePath = url.substring(url.indexOf("/")+1);
        InputStream inputStream = null;
        OutputStream os = null;
        try {
            String newName = URLEncoder.encode(name, "UTF-8");
            response.setHeader("Content-disposition", "attachment;filename="+newName);
            //打开本地文件流
            inputStream = 【获取远程服务器的输入流】;
            os = response.getOutputStream();
            //循环写入输出流
            byte[] b = new byte[1024];
            int length;
            while ((length = inputStream.read(b)) > 0) {
                os.write(b, 0, length);
            }
            os.flush();
        } catch (Exception e){
            e.printStackTrace();
        }finally {
            try {
                if(os!=null){
                    os.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
            try {
                if(inputStream!=null){
                    inputStream.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

        结果反馈:还是失败了,因为这个接口有权限限制,利用方案1的前台请求,没有办法加入权限验证

方案3:加入权限验证,转换blod格式,再利用canvas进行下载

        方案2的思路是没有错,我放开权限验证后,是跨域进行下载的,但是并不能完美的解决我要下载的需求,于是经过反复推敲,利用了多种方案的结合,先后进行了权限验证、数据格式转换、跨域响应和canvas下载。

后端代码:如方案2所示

前端代码:

//访问后端接口(权限验证),转换成blob的访问地址
function downloadPicture(url,mc){
    var xhr = new XMLHttpRequest();
    let path = '';
    xhr.open('GET', url, true);
    xhr.responseType = 'blob';
    xhr.setRequestHeader(【权限验证信息】);
    xhr.onload = function(res) {
        if (this.status === 200) {
            var type = xhr.getResponseHeader('Content-Type');
            var blob = new Blob([this.response], {type: type});
            if (typeof window.navigator.msSaveBlob !== 'undefined') {
                window.navigator.msSaveBlob(blob);
            } else {
                var URL = window.URL || window.webkitURL;
                path =  URL.createObjectURL(blob);
                console.log("path0:",path);
                imageToCanvas(path,mc);
            }
        }
    }
    xhr.send();
}
//利用canvas重画图片,跨域验证
function imageToCanvas(requestUrl,mc) {
    var image = new Image(120,120)
    image.setAttribute('crossOrigin', 'anonymous')
    image.onload = function () {
        var canvas = document.createElement('canvas')
        canvas.width = image.width
        canvas.height = image.height
        var context = canvas.getContext('2d')
        context.drawImage(image, 0, 0, image.width, image.height)
        var url = canvas.toDataURL('image/png')
        var a = document.createElement('a')
        var event = new MouseEvent('click')
        a.download = mc + '.png';
        a.href = url
        a.dispatchEvent(event);
    }
    image.src = requestUrl;
}

这个方案最后是解决问题,当然代码还有优化的地方,因为时间关系,我并没有再做后面的优化测试

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值