文件下载不就是个链接
测试平台有个对比功能要上传个文件,一向理解的文件下载,就是在前端设置个超链接,能链接到文件地址就可以下载了。于是就照着这个思路开始写,先是前端来个超链接,
简单。下面就是文件放置地址了,想过放前端,可是太不规范,我们还是要按规矩来,放到后端webapp目录下。放置好后,怎么拼装链接都拿不到文件,都是404,很小白的问题,后端没有暴露API,不可能这样直接能获取文件(其实配置tomcat成文件服务器也可以,本篇不讲)。
后端设置文件下载接口
那就增加个接口吧
@RequestMapping(value = "compare")
public void addDBConfig(HttpServletResponse response, HttpServletRequest request) {
// 从项目根目录开始获取文件,path必须是文件名,而不是上级目录
String filePath = System.getProperty("webapp.root") + "file/template.xlsx";
logger.info("获取文件路径:" + filePath);
String fileName = "template.xlsx";
String userAgent = request.getHeader("USER-AGENT");
boolean result = myDownLoad(filePath, fileName, userAgent, response);
if (!result)
logger.error("对比文件下载有误");
}
通用下载方法,后续有其他下载时可用。
重点关注是返回要设置头信息,文件要输出成流,以及编码统一防止乱码。
//下载方法
public boolean myDownLoad(String filePath, String fileName, String userAgent, HttpServletResponse response) {
File f = new File(filePath);
if (!f.exists()) {
try {
response.sendError(404, "File not found!");
} catch (IOException e) {
e.printStackTrace();
}
return false;
}
String type = fileName.substring(fileName.lastIndexOf(".") + 1);
//判断下载类型 xlsx 或 xls 现在只实现了xlsx、xls两个类型的文件下载
if (type.equalsIgnoreCase("xlsx") || type.equalsIgnoreCase("xls")) {
response.setContentType("application/octet-stream");
response.setCharacterEncoding("UTF-8");
// response.setContentType("application/force-download;charset=UTF-8");
try {
if (StringUtils.contains(userAgent, "MSIE") || StringUtils.contains(userAgent, "Edge")) {// IE浏览器
fileName = URLEncoder.encode(fileName, "UTF8");
} else if (StringUtils.contains(userAgent, "Mozilla")) {// google,火狐浏览器
fileName = new String(fileName.getBytes(), "ISO8859-1");
} else {
fileName = URLEncoder.encode(fileName, "UTF8");// 其他浏览器
}
response.setHeader("Content-disposition", "attachment; filename=" + fileName);
} catch (UnsupportedEncodingException e) {
logger.error(e.getMessage(), e);
return false;
}
InputStream in = null;
OutputStream out = null;
try {
//获取要下载的文件输入流
in = new FileInputStream(filePath);
int len = 0;
//创建数据缓冲区
byte[] buffer = new byte[1024];
//通过response对象获取outputStream流
out = response.getOutputStream();
//将FileInputStream流写入到buffer缓冲区
while ((len = in.read(buffer)) > 0) {
//使用OutputStream将缓冲区的数据输出到浏览器
out.write(buffer, 0, len);
}
//这一步走完,将文件传入OutputStream中后,页面就会弹出下载框
} catch (Exception e) {
logger.error(e.getMessage(), e);
return false;
} finally {
try {
if (out != null)
out.close();
if (in != null)
in.close();
} catch (IOException e) {
logger.error(e.getMessage(), e);
}
}
return true;
} else {
logger.error("不支持的下载类型!");
return false;
}
}
很好,调用链接能正常下载。
前后端部署的问题
开心的提交部署,咦,怎么下载不下来了。一顿排查,发现链接本地没问题,服务器也可以做到没问题,但问题是啥;服务器上部署的后端在tomcat下tank_be目录,本地是直接ROOT目录,前端设置的链接为固定链接,两边不一致就只能保证一边的正确性。
那其他前后端交互接口是如何操作的呢?在前端lib/axiosConfig.js里的设置前缀:
// 基础url前缀
baseURL: '/tank_be/tank/',
// baseURL: '/tank/',
只要前端发起的axios请求都能自动拼装前缀,发布到不同目录,修改这里配置即可。
好,那就把前端固定链接也改装成axios请求,链接也配置成el-link。
重点关注要把返回设置成blob文件形式。
<el-link type='primary' @click='download' title="下载">模板下载</el-link>
download () {
let config = {responseType: 'blob'}
axios
.get(`/download/compare`, config)
.then(data => {
if (!data) {
this.$message.error('模板下载异常')
}
let url = window.URL.createObjectURL(new Blob([data]))
let link = document.createElement('a')
link.style.display = 'none'
link.href = url
link.setAttribute('download', 'template.xlsx')
document.body.appendChild(link)
link.click()
})
},
如此,不管是服务器还是本地都能拼装链接,正常请求到后端下载文件。相信还有其他更方便的方式解决,等后续再来补充。
前端实现
后端实现