SpringBoot + Vue 前后端分离项目下载视频文件踩坑记录
时间:2022-07-26
本文章向大家介绍SpringBoot + Vue 前后端分离项目下载视频文件踩坑记录,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。
☞ 背景
项目服务端用的是 SpringBoot + SpringCloud + SpringDataJPA,前端用的是 Vue,有一个功能需要下载从监控中截取的视频(mp4),该视频由另一程序截取好放在某一目录下,使用 nginx 进行访问跳转,例如:视频:http://xx.xx.xx.xx:81/videoPath
、web: http://xx.xx.xx.xx
。
☞ 坑一:a 标签
let link = document.createElement('a')
link.setAttribute('download', this.videoPath)
link.style.display = 'none'
link.href = this.url
document.body.appendChild(link)
link.click();
使用该种方式直接将视频给打开了,没有下载视频,开始我以为是 download 属性写错了,后来发现是 url 指向第三方资源,download 属性失效了。
☞ 坑二:downloadjs 插件
百度了一圈解决方案之后发现了 downloadjs 插件,觉得挺不错然后后开始了踩坑。安装 npm install downloadjs --save
import download from 'downloadjs'
function() {
download(this.url);
}
emmmm… 不出意外,又掉坑里了,他说我跨域了!好嘛,那就解决跨域,nginx 来一波,成功弹出下载框了,看到秒下完,顿时感觉不对。一看只有几 K,不死心的点开看了下,接受了下图的嘲讽。
☞ 坑三:responseType: blob
经过一番查询没找到有效解决方案,加上时间问题我决定直接在后端写个 IO 来搞定下载问题。
@RestController
@RequestMapping("/download")
public class Download {
@PersistenceContext
private EntityManager entityManager;
@Value("${param.url.disk}")
public String disk;
@RequestMapping("/video")
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
String id = request.getParameter("id");
// 参数合法性
if (Objects.isNull(id) || StringUtils.isEmpty(id)) {
return;
}
BaseDAOImpl<Info, Long> infoDao = new BaseDAOImpl<>(Info.class, entityManager);
Info info = infoDao.findOne(Long.parseLong(id));
String filename = info.getVideoPath();
String realPath = disk + info.getPicturePath() + File.separatorChar + filename;
FileInputStream fis = new FileInputStream(realPath);
// 设置response的响应头
String mimeType = servletContext.getMimeType(filename);
response.setHeader("content-type",mimeType);
String agent = request.getHeader("user-agent");
filename = DownLoadUtils.getFileName(agent, filename);
response.setHeader("content-disposition","attachment;filename=" + filename);
ServletOutputStream sos = response.getOutputStream();
byte[] buff = new byte[1024 * 8];
int len = 0;
while((len = fis.read(buff)) != -1){
sos.write(buff,0,len);
}
fis.close();
}
}
operateEvent(data) {
down("/weChat/download/video" , {id: data.id}).then(response => {
this.saveAs(new Blob([response.data], { type: 'text/plain;charset=UTF-8' }), filename);
});
},
// 导出文件函数
saveAs (obj, fileName) {
let ele = document.createElement('a');
ele.download = fileName || '下载';
ele.href = URL.createObjectURL(obj); // 绑定a标签
ele.style.display = 'none';
document.body.appendChild(ele); // 兼容火狐浏览器
ele.click();
setTimeout(function () { // 延时释放
URL.revokeObjectURL(obj); // 用 URL.revokeObjectURL() 来释放这个object URL
document.body.removeChild(ele); // 兼容火狐浏览器
}, 100);
},
成功弹出下载框,但是!!!后缀居然是 txt,直接给我干蒙了,什么鬼,为什么会是 txt 文件,经过一番 debug 后,发现混进来一个奇怪的东西。
data居然乱码了。仔细查找一番后发现,在项目中我们封装了 HTTP 请求组件,他是这个坑的根源 ~ 有木有发现少了啥,没有写返回类型 ╯︿╰
export function fetch(url, data = {}) {
return new Promise((resolve, reject) => {
//debugger;
axios.defaults.headers["Authorization"] = "Bearer " + sessionStorage.getItem('token');
axios.defaults.headers["ClientType"] = global_variable.Wise.ClientType;
axios({url: base + url, method: 'GET', params: data})
.then(response => {
resolve(response);
})
.catch(error => {
reject(error);
})
});
}
赶紧写上一个给加上 responseType: ‘blob’,测试通过。对于毕业一年在一家小公司既写前端又写后端的我来说,真的是一把辛酸泪。
export function down(url, data = {}) {
return new Promise((resolve, reject) => {
axios.defaults.headers["Authorization"] = "Bearer " + sessionStorage.getItem('token');
axios.defaults.headers["ClientType"] = global_variable.Wise.ClientType;
axios({url: base + url, method: 'GET', params: data, responseType: 'blob'})
.then(response => {
resolve(response);
})
.catch(error => {
reject(error);
})
});
}
- 画廊视图Gallery
- WebForms使用System.Web.Routing
- 学Java到底好不好,已学未学将要学,大家都快看过来
- 理解SynchronizationContext
- WPF 的Main方法
- WiX制作安装包--系统必备
- 选项卡TabHost
- FPGA入门
- .NET 4 System.Threading.Barrier 类
- 终被捕!5名罗马尼亚黑客因入侵华盛顿政府摄像头被捕
- VUE 入门基础(9)
- 国家级人工智能产业基地落户松江,发布上海首款国产人工智能芯片
- C# 4.0 Optional Parameters 和Named Parameters
- 开源Web服务器GoAhead远程代码执行漏洞 影响数十万物联网设备
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- PAT (Basic Level) Practice (中文)1036 跟奥巴马一起编程 (15 分)
- PAT (Advanced Level) Practice 1024 Palindromic Number (25 分)
- Flink 连接 hive 解决 java.net.UnknownHostException
- PAT (Advanced Level) Practice 1147 Heaps (30 分)
- Java自动化测试(app自动化环境搭建 31)
- PAT (Basic Level) Practice (中文)1038 统计同成绩学生 (20 分)
- 数据结构题集(严书)串 常见习题代码
- PAT (Basic Level) Practice (中文)1040 有几个PAT (25 分)
- 201909-4ccf计算机职业资格认证考试 第四题 推荐系统
- 【Linux_Shell 脚本编程学习笔记四、监控系统内存并报警企业案例脚本】
- PAT (Basic Level) Practice (中文)1042 字符统计 (20 分)
- Pytorch 中的 5 个非常有用的张量操作
- k-近邻算法实现数字识别
- 【Linux_Shell 脚本编程学习笔记五、Oracle JDK1.8 安装shell 脚本】
- vue中子组件使用$emit传值的种种情况