持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第2天,点击查看活动详情
环境介绍
前后端分离,前端vue,后台springboot。
文件放置位置
位置1:
放在前端服务器,如/static/文件夹下。
(优缺点:方便管理,无需接口调用;但是安全性不高,直接输入文件位置即可访问;适用于安全性要求不高,无需动态修改文件内容的指定文件。)
位置2:
放在后台服务器,指定位置或者jar包中。
(优缺点:需要后台提供接口调用,安全性高,可校验用户权限后在确认打开或下载文件;适用于安全性有要求,需要动态修改文件内容的文件。)
打开还是下载
前端浏览器
通常情况下浏览器只支持.pdf格式,可以直接打开在线预览,再下载。
如果是.xls等格式,默认直接下载,且无法修改名字和路径。
后台接口调用
若调用后台接口打开.pdf格式文件,打开还是下载取决于返回消息头的设置。
// 直接下载文件
response.addHeader("Content-Disposition", "attachment;fileName=" + fileName); // fileName设置文件名
// 在浏览器端直接打开文件
response.addHeader("Content-Disposition", "inline;fileName=" + fileName); // fileName设置文件名
方法一(前端在线打开静态资源文件)
1、a标签,点击下载
<a href="/static/用户手册.pdf" target="_black">文档的打开与下载</a>
若有属性download="filename"
,强制下载,若无,根据target属性判断如何打开。
- 但是官方也说了只有 Firefox 和 Chrome 支持 download 属性。
- 详见文档:www.w3school.com.cn/tags/att_a_…
- 若要考虑兼容性咋办呢,尝试了半天好像也没什么结果。
- 只能通过接口调用解决,详见另一篇文章:juejin.cn/post/710933…
常用target属性如下:
target="_self"
,默认。在相同的框架中打开被链接文档。target="_blank"
,在新窗口中打开被链接文档。
2、embed标签,嵌入页面
<embed src="/static/用户手册.pdf">
IE不支持,需要搭配object等一系列标签使用。
3、iframe标签,嵌入页面
<iframe src="/static/用户手册.pdf" width="100%" height="100%"></iframe>
IE不支持,非.pdf文件会提示下载或只读打开。
方法二(调用后台接口)
1、window.open接口直接下载
window.open(url + '?name=用户手册.pdf') // url为后台提供的接口
注意:window.open默认是get请求
2、获取文件流,前端处理下载
let url = this.$_apiUrl.curveHistoryReport
let params = {
als: this.cycle,
lts: this.dataTime[0],
lte: this.dataTime[1],
labels: labelArr
}
this.$axiosDownload(url, params, '历史曲线数据视图', '.xlsx')
export const axiosDownload = (url, params, fileName, fileType) => {
return new Promise((resolve, reject) => {
axios({
method: 'post',
url: url,
data: params, // 注意post需要是data
responseType: 'blob'
}).then((res) => {
if (!res) {
resolve(0)
} else {
let disp = res.headers['content-disposition'].split(';')
let filename = ''
let filetype = ''
// 后台返回的文件名和类型
for (let i = 0; i < disp.length; i++) {
if (disp[i].indexOf('filename=') !== -1) {
let fileinfo = disp[i].replace('filename=', '')
let filearr = fileinfo.split('.')
filetype = '.' + filearr[filearr.length - 1]
filename = fileinfo.replace(filetype, '')
break
}
}
// 如果前端传入了文件名\类型以前端为准
if (fileName) filename = fileName
if (fileType) filetype = fileType
// 解析文件
let blob = new Blob([res.data], {
type: 'application/octet-stream'
})
let url = window.URL.createObjectURL(blob)
let link = document.createElement('a')
link.style.display = 'none'
link.href = url
link.setAttribute('download', decodeURI(filename + filetype)) // 文件名和后缀从后台获取
document.body.appendChild(link)
link.click()
document.body.removeChild(link)
resolve(1)
}
}).catch(() => {
resolve(0)
})
})
}
注意:
- 若后台提供的接口类型是get,对应axios请求参数属性是params;
- 若后台提供的接口类型是post,对应axios请求参数属性是data。
方法N(未尝试)
- 1、pdf.js插件
- 2、pdf2swf工具
- 3、转换为SWF,使用FlexPa在线浏览
附打开下载关键代码
前端接口调用
1、获取文件blob大对象
// 获取blob大对象
export const axiosBlob = (url, params, callback, plus) => {
return new Promise((resolve, reject) => {
axios({
method: 'get',
url: url,
params: params,
responseType: 'arraybuffer'
}).then((res) => {
if (!res) {
resolve(0)
} else {
let blob = new Blob([res.data], {
type: 'application/octet-stream'
})
let fileReader = new FileReader()
fileReader.readAsDataURL(blob) // readAsDataURL
fileReader.onload = (e) => {
if (typeof callback === 'function') { callFunction(callback, e.target.result, plus) }
}
fileReader.onerror = () => {
reject(new Error('blobToBase64 error'))
}
resolve(1)
}
}).catch(() => {
resolve(0)
})
})
}
2、获取blob的url
// 文件加载 提供在线打开 返回blob的url
export const axiosLoad = (url, params, callback) => {
return new Promise((resolve, reject) => {
axios({
method: 'get',
url: url,
params: params, // get的数据是params接收 post的数据是data接收!!!
responseType: 'blob'
}).then((res) => {
if (!res) {
resolve(0)
} else {
let blob = new Blob([res.data], {
type: 'application/octet-stream'
})
let loadUrl = window.URL.createObjectURL(blob)
if (typeof callback === 'function') { callFunction(callback, loadUrl) }
resolve(1)
}
}).catch(() => {
resolve(0)
})
})
}
3、获取文档直接下载
// 文件下载
// resolve方法用于axiosDownload.then捕获
// reject方法用于axiosDownload.catch捕获
// 该处都使用resolve(e) 即调用axiosDownload完成后可以.then(e)进行异步处理
export const axiosDownload = (url, params, fileName, fileType) => {
return new Promise((resolve, reject) => {
axios({
method: 'get',
url: url,
params: params,
responseType: 'blob'
}).then((res) => {
if (!res) {
resolve(0)
} else {
let disp = res.headers['content-disposition'].split(';')
let filename = ''
let filetype = ''
// 后台返回的文件名和类型
for (let i = 0; i < disp.length; i++) {
if (disp[i].indexOf('filename=') !== -1) {
let fileinfo = disp[i].replace('filename=', '')
let filearr = fileinfo.split('.')
filetype = '.' + filearr[filearr.length - 1]
filename = fileinfo.replace(filetype, '')
break
}
}
// 如果前端传入了文件名\类型以前端为准
if (fileName) filename = fileName
if (fileType) filetype = fileType
// 解析文件
let blob = new Blob([res.data], {
type: 'application/octet-stream'
})
let url = window.URL.createObjectURL(blob)
let link = document.createElement('a')
link.style.display = 'none'
link.href = url
link.setAttribute('download', decodeURI(filename + filetype)) // 文件名和后缀从后台获取
document.body.appendChild(link)
link.click()
document.body.removeChild(link)
resolve(1)
}
}).catch(() => {
resolve(0)
})
})
}
JAVA文档的打开与下载工具
public void fileDownloadByName(HttpServletResponse response,String name) throws Exception {
String fileName = URLEncoder.encode(name, "utf-8");
ClassPathResource resource = new ClassPathResource("/" + name);
if (resource.getInputStream() != null) {
fileUtil(resource.getInputStream(), response, fileName);
}
}
public void fileUtil(InputStream fis,HttpServletResponse response,String fileName) {
response.setContentType("application/force-download"); // 强制下载不打开
response.addHeader("Content-Disposition", "attachment;fileName=" + fileName); // 设置文件名
byte[] buffer = new byte[1024];
BufferedInputStream bis = null;
try {
bis = new BufferedInputStream(fis);
OutputStream os = response.getOutputStream();
int i = bis.read(buffer);
while (i != -1) {
os.write(buffer, 0, i);
i = bis.read(buffer);
}
log.debug("下载成功");
} catch (Exception e) {
log.error("下载失败:" + e);
} finally {
if (bis != null) {
try {
bis.close();
} catch (IOException e) {
log.error("下载失败:" + e);
}
}
if (fis != null) {
try {
fis.close();
} catch (IOException e) {
log.error("下载失败:" + e);
}
}
}
}