问题描述:
当图片url是同源时(没有跨域),要实现下载功能,在a标签加入download属性即可
<a href='图片url' download='图片名称【可加可不加】'></a>
当图片url与前端应用服务非同源时(跨域请求),使用上面的方案就会出现:没有下载,打开了一个类似预览功能的窗口。
在网上查询资料,尝试了以下几种方案
目录
方案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;
}
这个方案最后是解决问题,当然代码还有优化的地方,因为时间关系,我并没有再做后面的优化测试