一、问题描述:
使用若依前后端分离项目,前台点击下载图片,触发下载事件,而
不是直接打开显示图片
二、解决思路:
前端调用后端自定义下载接口,将需要下载的图片https链接传给后端下载,将二进制(blob)文件流返回给前端,前端用a标签下载
- 后端需要设置响应头
response.addHeader("Access-Control-Allow-Origin", "*");
允许跨域
前端需要设置responseType: 'blob'
告诉后端前端需要的是blob二进制文件流,否则会导致返回乱码- 请求方式一、前端调用request.js方式封装的发送请求方式调用后端自定义下载接口/common/download/url
或者
请求方式二、前端发送请求调用后端自定义文件下载接口/common/download/url的代码可以参考:链接指引,下面也有给出
三、前端代码:
①、标签:
<template slot-scope="scope">
<el-button
id="IframeReportImg"
size="mini"
type="text"
icon="el-icon-download"
@click="handleUpload(scope.row)"
>下载
</el-button>
</template>
②、请求方式一:用request.js封装的请求后端下载接口
//下载图片
export function downloadImgs(params) {
return request({
url: '/common/download/url',//后端自定义的下载接口/common/download/url,路径为admin模块的com.ruoyi.web.controller.common;
responseType: 'blob', //设置后端返回类型为二进制流,不设置会返回乱码
method: 'get',
params
})
}
handleUpload(row) {
const url = row.processedImg//图片的https链接
const imgName = url.substr(url.lastIndexOf('/') + 1)
downloadImgs({
url: url
})
.then(res => {
let href = window.URL.createObjectURL(new Blob([res])) // 根据后端返回的url对应的文件流创建URL对象
const link = document.createElement('a')//创建一个隐藏的a标签
link.style.display = 'none'
link.href = href//设置下载的url
link.download = imgName //设置下载的文件名
document.body.appendChild(link)
link.click()
document.body.removeChild(link)
window.URL.revokeObjectURL(href) // 释放掉blob对象
})
},
③、请求方式二:直接封装请求后端下载接口
handleUpload(row) {
var url = 'http://后端服务器ip地址:8090/common/download/url?url=' + 图片https链接
var fileName = url.substr(url.lastIndexOf('/') + 1)
const xhr = new XMLHttpRequest()
xhr.open('get', url)
xhr.responseType = 'blob'//设置后端返回类型为二进制流,不设置会返回乱码
xhr.setRequestHeader('Authorization', 'Bearer ' + getToken()) // 若依的鉴权token
xhr.onreadystatechange = function() {
if (this.readyState === 4 && this.status === 200) {
const name = fileName || xhr.getResponseHeader('Content-Disposition').split('filename=')[1]
// 支持IE浏览器
if (window.navigator && window.navigator.msSaveOrOpenBlob) {
window.navigator.msSaveOrOpenBlob(this.response, name)
return
}
const href = window.URL.createObjectURL(this.response)
const link = document.createElement('a')
link.style.display = 'none'
link.href = href
link.setAttribute('download', name)
document.body.appendChild(link)
link.click()
document.body.removeChild(link)
window.URL.revokeObjectURL(href) // 释放掉blob对象
}
}
xhr.send()
},
四、后端自定义下载接口/common/download/url
代码:
/**
* description:根据url下载返回给前端二进制文件流
*
* @param request
* @param response
* @param url
*/
@GetMapping("/common/download/url")
public void downLoadFromUrl(HttpServletRequest request, HttpServletResponse response, String url) {
try {
URL urls = new URL(url);
// 根据url创建URL对象
BufferedInputStream bis = new BufferedInputStream(urls.openStream());
byte[] tmp = new byte[1024];
String name = url.substring(url.lastIndexOf('/') + 1);
response.reset();
response.setCharacterEncoding("UTF-8");
response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE);
response.addHeader("Access-Control-Allow-Origin", "*");//后端允许跨域
response.addHeader("Access-Control-Expose-Headers", "Content-Disposition,download-filename");
// 将文件名转成utf8的字符串形式
response.addHeader("Content-Disposition", "attachment;filename=" + new String(name.getBytes(), "utf-8"));
// 获取输出流,将文件流返回给前端
OutputStream out = response.getOutputStream();
int len = 0;
// 从url指向的链接中读取数据保存到tmp字节数组中,写入到输出流中
while ((len = bis.read(tmp, 0, 1024)) != -1) {
out.write(tmp, 0, len);
}
bis.close();
out.close();
} catch (IOException e) {
log.error("下载文件失败", e);
}
}