阿里云视频上传视频获取进度条问题(使用session方案,获取进度一直为0的解决方案)补充:前后端分离项目中获取进度解决方案
1.场景描述:
之前用阿里云上传视频,前端反应上传视频经常出现获取视频url失败问题.但是接口我测过很多遍都是没有问题的.后台这边提供了一个视频上传的接口返回一个videoId,还提供了一个根据videoId获取视频url的接口.前端把两个接口的调用都封装在一个方法中,视频上传完成之后直接根据视频id获取视频url.这样会出现一个问题,视频文件如果时间太长,所以前端页面会请求超时,所以会上传失败.商量了一下就是将接口分开来,页面上除了有一个上传的接口,单独提供一个点击查看上传视频的按钮,分别调用两个接口.但是这样问题又来了,如何保证用户上传之后再点击查看按钮调用根据视频id获取视频url的接口,解决方案就是添加一个进度条,显示一下阿里云上传文件的进度.
2.添加上传进度过程中遇到的问题以及解决方案
①阿里云官方的api中有进度回调方法,但是只能是从服务端获取到,如何实时传递到浏览器端.自己想到的方案:第一种是使用redis存储这种随时变化的数据(由于项目中没有使用redis,所以鉴于搭建redis环境考虑就果断放弃此方案);第二种是使用数据库存储(由于对数据库添加以及删除操作过于频繁所有放弃此方案);第三方就是数据存储于服务器端的session解决方案更适合本场景.这也是网上使用很多的方案.具体实现逻辑:除了原有两个接口之外需要添加一个获取上传进度的接口,页面添加一个定时器需要定时的请求此接口刷新进度条,实现上传进度显示.
②技术方案定好之后遇到的问题:每次调用获取进度的接口返回的进度都是0,但是实际上视频都是正常上传的,上传的进度都能通过日志进行打印.阿里云进度监听的类中确实把进度存入session中了.原因:调试发现请求进度接口中的session与上传文件的请求中根据request获取的session不是一个session对象.这就导致每次从session中获取都是0,这里需要说一下session的获取问题.
2.1首先说一下使用request获取session的方式以及区别:
request.getSession(true):若存在会话则返回该会话,否则新建一个会话(request.getSession()默认就是true,通过查看源码可见)。
request.getSession(false):若存在会话则返回该会话,否则返回NULL;
2.2获取session原理:浏览器与服务端交互时,第一次发送请求时会创建session对象,返回JssionId信息并缓存在浏览器中cookie中,下次发送请求会携带该jssionId,使用request获取session对象时会根据是否有jsessionId判断直接获取session对象还是直接创建.
2.3至于两个请求中request如何保证session对象唯一在于:上传文件接口中虽然使用request获取session并往session中添加进度信息,但是此时调用获取进度接口中根据request获取session对象由于不存在JssionId(因为上传接口还未结束,不会响应JssionId信息),所以会重新创建一个session对象.
2.4此问题的解决方案就是保证两个接口通过request获取的session一致,我想到的解决方案就是,先调用获取进度接口,此时返回的进度是0,但是浏览器也缓存了jessionId信息,然后调用文件上传接口,请求中会携带该jessionId,此时文件上传接口中使用request获取的session一定与获取进度接口中的session对象一致.当然要注意在文件上传成功之后请求session信息,否则下次请求进度接口显示的进度是从百分之百开始的了.此处使用的是session自杀方式:session.invalidate();
3.部分主要代码:
文件上传逻辑:
public ResultVo uploadVideo(String title,String fileName,HttpServletRequest httpServletRequest) {ResultVo resultVo = new ResultVo();// 判空处理if(StringUtils.isEmpty(title)||StringUtils.isEmpty(fileName)){resultVo.setSuccess(false);resultVo.setMsg("视频上传失败:视频标题或是视频文件路径为空");return resultVo;}// 文件类型校验// 对上传的视频格式进行校验: 这里简单校验后缀名boolean isLegal = false;for (String type : VIDEO_TYPE) {if (StringUtils.endsWithIgnoreCase(fileName, type)) {isLegal = true;break; // 只要与允许上传格式其中一个匹配就可以}}// 格式错误, 返回与前端约定的errorif (!isLegal) {resultVo.setSuccess(false);resultVo.setMsg("视频上传失败:"+"视频格式错误,支持格式:.mp4,.rmvb,.rm,.avi,.flv");return resultVo;}UploadVideoRequest request = new UploadVideoRequest(aliyunConfig.getVideoAccessKeyId(), aliyunConfig.getVideoAccessKeySecret(), title, fileName);/* 可指定分片上传时每个分片的大小,默认为1M字节 *///request.setPartSize(1 * 1024 * 1024L);/* 可指定分片上传时的并发线程数,默认为1,(注:该配置会占用服务器CPU资源,需根据服务器情况指定)*///request.setTaskNum(1);/* 是否开启断点续传, 默认断点续传功能关闭。当网络不稳定或者程序崩溃时,再次发起相同上传请求,可以继续未完成的上传任务,适用于超时3000秒仍不能上传完成的大文件。注意: 断点续传开启后,会在上传过程中将上传位置写入本地磁盘文件,影响文件上传速度,请您根据实际情况选择是否开启*///request.setEnableCheckpoint(false);/* OSS慢请求日志打印超时时间,是指每个分片上传时间超过该阈值时会打印debug日志,如果想屏蔽此日志,请调整该阈值。单位: 毫秒,默认为300000毫秒*///request.setSlowRequestsThreshold(300000L);/* 可指定每个分片慢请求时打印日志的时间阈值,默认为300s*///request.setSlowRequestsThreshold(300000L);/* 是否使用默认水印(可选),指定模板组ID时,根据模板组配置确定是否使用默认水印*///request.setIsShowWaterMark(true);/* 自定义消息回调设置(可选),参数说明参考文档 https://help.aliyun.com/document_detail/86952.html#UserData */// request.setUserData("{\"MessageCallback\":{\"CallbackURL\":\"http://hicgu6.natappfree.cc/course/list?pageCurrent=1&pageNum=2\"}}");/* 视频分类ID(可选) *///request.setCateId(0);/* 视频标签,多个用逗号分隔(可选) *///request.setTags("标签1,标签2");/* 视频描述(可选) *///request.setDescription("视频描述");/* 封面图片(可选) *///request.setCoverURL("http://cover.sample.com/sample.jpg");/* 模板组ID(可选) *///request.setTemplateGroupId("8c4792cbc8694e7084fd5330e56a33d");/* 存储区域(可选) *///request.setStorageLocation("https://yujia-shanghai-bucket.oss-cn-shanghai.aliyuncs.com/kawa2/course-image/");// request.setStorageLocation("outin-7908e57a68eb11e9a8d800163e1a625e.oss-cn-shanghai.aliyuncs.com");request.setStorageLocation(aliyunConfig.getVideoLocation());/* 开启默认上传进度回调 */request.setPrintProgress(true);/* 设置自定义上传进度回调 (必须继承 ProgressListener) */HttpSession session = httpServletRequest.getSession();request.setProgressListener(new PutObjectProgressListener(session));UploadVideoImpl uploader = new UploadVideoImpl();UploadVideoResponse response = uploader.uploadVideo(request);//System.out.print("RequestId=" + response.getRequestId() + "\n"); //请求视频点播服务的请求IDif (response.isSuccess()) {resultVo.setSuccess(true);resultVo.setMsg("视频上传成功");//resultVo.setData("videoId:"+response.getVideoId()+",requestId:"+response.getRequestId());resultVo.setData("videoId:"+response.getVideoId());// 视频上传成功之后清除session对象session.invalidate();} else {/* 如果设置回调URL无效,不影响视频上传,可以返回VideoId同时会返回错误码。其他情况上传失败时,VideoId为空,此时需要根据返回错误码分析具体错误原因 */resultVo.setSuccess(false);resultVo.setMsg("视频上传失败");resultVo.setData("VideoId=" + response.getVideoId()+",ErrorCode=" + response.getCode()+",ErrorMessage=" + response.getMessage());}return resultVo;}
阿里云进度回调逻辑:
public class PutObjectProgressListener implements VoDProgressListener {/*** 已成功上传至OSS的字节数*/private long bytesWritten = 0;/*** 原始文件的总字节数*/private long totalBytes = -1;/*** 本次上传成功标记*/private boolean succeed = false;/*** 视频ID*/private String videoId;/*** 图片ID*/private String imageId;// 使用session存储文件上传进度private HttpSession session;private int uploadPercent = 0;//构造方法中加入sessionpublic PutObjectProgressListener(){}public PutObjectProgressListener(HttpSession mSession) {this.session = mSession;session.setAttribute("upload_percent", uploadPercent);}public void progressChanged(ProgressEvent progressEvent) {long bytes = progressEvent.getBytes();ProgressEventType eventType = progressEvent.getEventType();switch (eventType) {// 开始上传事件case TRANSFER_STARTED_EVENT:if (videoId != null) {System.out.println("Start to upload videoId " + videoId + "......");}if (imageId != null) {System.out.println("Start to upload imageId " + imageId + "......");}break;// 计算待上传文件总大小事件通知,只有调用本地文件方式上传时支持该事件case REQUEST_CONTENT_LENGTH_EVENT:this.totalBytes = bytes;System.out.println(this.totalBytes + "bytes in total will be uploaded to OSS.");break;// 已经上传成功文件大小事件通知case REQUEST_BYTE_TRANSFER_EVENT:this.bytesWritten += bytes;if (this.totalBytes != -1) {int percent = (int) (this.bytesWritten * 100.0 / this.totalBytes);// 将进度添加到session中//将进度percent放入session中session.setAttribute("upload_percent", percent);System.out.println(bytes + " bytes have been written at this time, upload progress: " +percent + "%(" + this.bytesWritten + "/" + this.totalBytes + ")");} else {System.out.println(bytes + " bytes have been written at this time, upload sub total : " +"(" + this.bytesWritten + ")");}break;// 文件全部上传成功事件通知case TRANSFER_COMPLETED_EVENT:this.succeed = true;if (videoId != null) {System.out.println("Succeed to upload videoId " + videoId + " , " + this.bytesWritten + " bytes have been transferred in total.");}if (imageId != null) {System.out.println("Succeed to upload imageId " + imageId + " , " + this.bytesWritten + " bytes have been transferred in total.");}break;// 文件上传失败事件通知case TRANSFER_FAILED_EVENT:if (videoId != null) {System.out.println("Failed to upload videoId " + videoId + " , " + this.bytesWritten + " bytes have been transferred.");}if (imageId != null) {System.out.println("Failed to upload imageId " + imageId + " , " + this.bytesWritten + " bytes have been transferred.");}break;default:break;}}public boolean isSucceed() {return succeed;}public void onVidReady(String videoId) {setVideoId(videoId);}public void onImageIdReady(String imageId) {setImageId(imageId);}public String getVideoId() {return videoId;}public void setVideoId(String videoId) {this.videoId = videoId;}public String getImageId() {return imageId;}public void setImageId(String imageId) {this.imageId = imageId;}
}
请求上传进度接口:
@RequestMapping ("/percent")@ResponseBodypublic int getUploadPercent(HttpServletRequest request){HttpSession session = request.getSession();int percent = session.getAttribute("upload_percent") == null ? 0: (Integer)session.getAttribute("upload_percent");return percent;}
根据文件上传成功返回的视频id获取视频url接口暂不提供,可参考阿里云api;
4.调用逻辑说明:
调用请求进度接口(确保session对象唯一)–>前端页面调用文件上传接口–>定时请求请求进度接口,实时显示上传进度.–>根据视频id获取视频url接口(视频上传完成之后查看上传视频内容)
5.补充一种使用场景(前后端分离项目)
上面说的解决方案适用于前后端一体项目,如果前后端分离的项目介绍一下我的解决方案:采用redis(此处使用jedis客户端进行存取).直接上代码
文件上传代码:
public ResultVo uploadVideo(String title,String fileName,HttpServletRequest httpServletRequest) {ResultVo resultVo = new ResultVo();// 判空处理if(StringUtils.isEmpty(title)||StringUtils.isEmpty(fileName)){resultVo.setSuccess(false);resultVo.setMsg("视频上传失败:视频标题或是视频文件路径为空");return resultVo;}// 文件类型校验// 对上传的视频格式进行校验: 这里简单校验后缀名boolean isLegal = false;for (String type : VIDEO_TYPE) {if (StringUtils.endsWithIgnoreCase(fileName, type)) {isLegal = true;break; // 只要与允许上传格式其中一个匹配就可以}}// 格式错误, 返回与前端约定的errorif (!isLegal) {resultVo.setSuccess(false);resultVo.setMsg("视频上传失败:"+"视频格式错误,支持格式:.mp4,.rmvb,.rm,.avi,.flv");return resultVo;}UploadVideoRequest request = new UploadVideoRequest(aliyunConfig.getVideoAccessKeyId(), aliyunConfig.getVideoAccessKeySecret(), title, fileName);/* 可指定分片上传时每个分片的大小,默认为1M字节 *///request.setPartSize(1 * 1024 * 1024L);/* 可指定分片上传时的并发线程数,默认为1,(注:该配置会占用服务器CPU资源,需根据服务器情况指定)*///request.setTaskNum(1);/* 是否开启断点续传, 默认断点续传功能关闭。当网络不稳定或者程序崩溃时,再次发起相同上传请求,可以继续未完成的上传任务,适用于超时3000秒仍不能上传完成的大文件。注意: 断点续传开启后,会在上传过程中将上传位置写入本地磁盘文件,影响文件上传速度,请您根据实际情况选择是否开启*///request.setEnableCheckpoint(false);/* OSS慢请求日志打印超时时间,是指每个分片上传时间超过该阈值时会打印debug日志,如果想屏蔽此日志,请调整该阈值。单位: 毫秒,默认为300000毫秒*///request.setSlowRequestsThreshold(300000L);/* 可指定每个分片慢请求时打印日志的时间阈值,默认为300s*///request.setSlowRequestsThreshold(300000L);/* 是否使用默认水印(可选),指定模板组ID时,根据模板组配置确定是否使用默认水印*///request.setIsShowWaterMark(true);/* 自定义消息回调设置(可选),参数说明参考文档 https://help.aliyun.com/document_detail/86952.html#UserData */// request.setUserData("{\"MessageCallback\":{\"CallbackURL\":\"http://hicgu6.natappfree.cc/course/list?pageCurrent=1&pageNum=2\"}}");/* 视频分类ID(可选) *///request.setCateId(0);/* 视频标签,多个用逗号分隔(可选) *///request.setTags("标签1,标签2");/* 视频描述(可选) *///request.setDescription("视频描述");/* 封面图片(可选) *///request.setCoverURL("http://cover.sample.com/sample.jpg");/* 模板组ID(可选) *///request.setTemplateGroupId("8c4792cbc8694e7084fd5330e56a33d");/* 存储区域(可选) *///request.setStorageLocation("https://yujia-shanghai-bucket.oss-cn-shanghai.aliyuncs.com/kawa2/course-image/");// request.setStorageLocation("outin-7908e57a68eb11e9a8d800163e1a625e.oss-cn-shanghai.aliyuncs.com");request.setStorageLocation(aliyunConfig.getVideoLocation());/* 开启默认上传进度回调 */request.setPrintProgress(true);/* 设置自定义上传进度回调 (必须继承 ProgressListener) */request.setProgressListener(new PutObjectProgressListener());UploadVideoImpl uploader = new UploadVideoImpl();UploadVideoResponse response = uploader.uploadVideo(request);//System.out.print("RequestId=" + response.getRequestId() + "\n"); //请求视频点播服务的请求IDif (response.isSuccess()) {resultVo.setSuccess(true);resultVo.setMsg("视频上传成功");//resultVo.setData("videoId:"+response.getVideoId()+",requestId:"+response.getRequestId());resultVo.setData("videoId:"+response.getVideoId());} else {/* 如果设置回调URL无效,不影响视频上传,可以返回VideoId同时会返回错误码。其他情况上传失败时,VideoId为空,此时需要根据返回错误码分析具体错误原因 */resultVo.setSuccess(false);resultVo.setMsg("视频上传失败");resultVo.setData("VideoId=" + response.getVideoId()+",ErrorCode=" + response.getCode()+",ErrorMessage=" + response.getMessage());}return resultVo;}
进度回调类中将进度添加到缓存中:
public class PutObjectProgressListener implements VoDProgressListener {/*** 已成功上传至OSS的字节数*/private long bytesWritten = 0;/*** 原始文件的总字节数*/private long totalBytes = -1;/*** 本次上传成功标记*/private boolean succeed = false;/*** 视频ID*/private String videoId;/*** 图片ID*/private String imageId;// add by txm 2020/11/19 视频上传进度使用redis方案解决 private Jedis jedis = new Jedis("redisIP", redis端口);public void progressChanged(ProgressEvent progressEvent) {long bytes = progressEvent.getBytes();ProgressEventType eventType = progressEvent.getEventType();switch (eventType) {// 开始上传事件case TRANSFER_STARTED_EVENT:if (videoId != null) {System.out.println("Start to upload videoId " + videoId + "......");}if (imageId != null) {System.out.println("Start to upload imageId " + imageId + "......");}break;// 计算待上传文件总大小事件通知,只有调用本地文件方式上传时支持该事件case REQUEST_CONTENT_LENGTH_EVENT:this.totalBytes = bytes;System.out.println(this.totalBytes + "bytes in total will be uploaded to OSS.");break;// 已经上传成功文件大小事件通知case REQUEST_BYTE_TRANSFER_EVENT:this.bytesWritten += bytes;if (this.totalBytes != -1) {int percent = (int) (this.bytesWritten * 100.0 / this.totalBytes);System.out.println(bytes + " bytes have been written at this time, upload progress: " +percent + "%(" + this.bytesWritten + "/" + this.totalBytes + ")");// add by txm 2020/11/19 redis解决视频上传进度问题jedis.set("progressData",String.valueOf(percent));} else {System.out.println(bytes + " bytes have been written at this time, upload sub total : " +"(" + this.bytesWritten + ")");}break;// 文件全部上传成功事件通知case TRANSFER_COMPLETED_EVENT:this.succeed = true;if (videoId != null) {System.out.println("Succeed to upload videoId " + videoId + " , " + this.bytesWritten + " bytes have been transferred in total.");}if (imageId != null) {System.out.println("Succeed to upload imageId " + imageId + " , " + this.bytesWritten + " bytes have been transferred in total.");}break;// 文件上传失败事件通知case TRANSFER_FAILED_EVENT:if (videoId != null) {System.out.println("Failed to upload videoId " + videoId + " , " + this.bytesWritten + " bytes have been transferred.");}if (imageId != null) {System.out.println("Failed to upload imageId " + imageId + " , " + this.bytesWritten + " bytes have been transferred.");}break;default:break;}}public boolean isSucceed() {return succeed;}public void onVidReady(String videoId) {setVideoId(videoId);}public void onImageIdReady(String imageId) {setImageId(imageId);}public String getVideoId() {return videoId;}public void setVideoId(String videoId) {this.videoId = videoId;}public String getImageId() {return imageId;}public void setImageId(String imageId) {this.imageId = imageId;}
}
控制层获取缓存中进度的逻辑:
private Jedis jedis = new Jedis("redis服务器IP", redis服务器端口);
@RequestMapping ("/percent")@ResponseBodypublic ResultVo getUploadPercent(HttpServletRequest request){String progressData = jedis.get("progressData");return ResultVoUtil.success(progressData,"获取上传进度成功");}
前后端分离的项目进行数据共享的方式还有很多,有时间会进行更新说明.
阿里云视频上传视频获取进度条问题(使用session方案,获取进度一直为0的解决方案)补充:前后端分离项目中获取进度解决方案相关推荐
- springboot前后端分离项目MultipartFile获取前端传的file为null问题
[问题描述] springboot+vue 前后端分离项目,在对接上传文件功能时,一直显示文件为空. [分析] 查找了很多博客,解决方法五花八门,但原因应该还是 springboot自带的org.sp ...
- 阿里云视频点播上传视频和获取视频播放地址
参考官方文档: Python上传SDK-普通文件上传.获取播放地址 本文只对上传视频和获取单个视频播放地址操作.更多案例如批量获取所有视频列表等操作请直接参考官方文档: 媒体管理 一.上传视频:上传完 ...
- 阿里云SDK上传视频
1.老样子,先看效果图 2.首先到阿里云下载所需要用到的SDK 3.下载好的,解压之后,目录以及运行起来是以下这个样子的 4.在实际项目中引用,先将SDK添加到项目中,放到public目录下 5.在p ...
- 使用阿里云视频点播上传视频
目录 前言 一.目的 二.实现步骤 1.准备操作 2.主要使用模块 2.1. 上传音频 2.2. 添加转码设置 3.搭建视频点播环境 3.1.配置Maven仓库 3.2.添加jar依赖 3.3.上传视 ...
- 【springboot】实现阿里云视频点播上传视频
阿里云视频点播 1.开通视频点播服务,选择按流量计费,对于个人项目来说,相当于免费. 2.开启存储管理 3.设置转码模板组.设置加密,如果不加密,别人只要获取到你视频的url就可以免费观看,但进行了加 ...
- 小程序中使用阿里云oss上传视频怎么获得视频截图?
在视频地址后面加上如下的后缀,就可以获取视频内部帧图片,当作封面使用. ?x-oss-process=video/snapshot,t_1000,m_fast ?x-oss-process=video ...
- spring boot 整合 阿里云oss上传
Spring Boot 整合 阿里云OSS上传 OSS申请和配置 1. 注册登录 2.开通以及配置 springboot整合使用 1. 进入我们springboot的项目中,导入oss相关依赖 2. ...
- 前后端分离项目,前端cookie获取不到的解决方案
最近在写前后端分离项目中发现,后端在response中添加cookie后 response.addCookie(cookie) 前端却无法拿到cookie,但是在网络抓包中确实存在cookie 但是在 ...
- web利用腾讯云点播上传视频
web利用腾讯云点播上传视频到云服务器 第一步导入 <script src="//imgcache.qq.com/open/qcloud/js/vod/sdk/ugcUploader. ...
最新文章
- C++中 int char 的相互转换
- C++Tower of Hanoi汉诺塔的实现算法(附完整源码)
- centos 安装php扩展gd,linux(centos)下为php添加添加GD扩展
- 税收分类编码2020_增值税开票系统你会吗?2020最新开票(金税盘版)图文教程详细版...
- Spring Boot文档阅读笔记-构建Restful风格的WebService
- PWDX查找程序执行路径
- 四年磨一剑:我是如何拿到蚂蚁 Offer 的?
- 已解决:录屏软件录不了全屏的问题
- Redis | 哨兵模式-Sentinel
- IDEA开发Springboot时,远程使用企业内部不能连接数据库的解决方法
- html自动补位的功能,js中位数不足自动补位扩展padLeft、padRight实现代码
- 找出1000瓶牛奶中有毒的牛奶
- 卸载百度软件修复服务器,快速修复win7系统笔记本卸载百度软件中心助手的解决教程...
- [转]中英文停止词表(stopword)
- 异贝,移动互联网技术,为中小微实体企业联盟、线上链接、线上线下自定义营销方案推送。案例42
- 【哈希表】(一) 设计哈希表
- 合肥通用职业技术学院计算机专业,2020年安徽高考专科提前批及大专录取时间及录取结果查询...
- 爬去微信小程序服务器代码,Python爬取微信小程序通用方法代码实例详解
- 海康威视人脸识别门禁系统对接
- 为什么说创意是一部宣传片的灵魂
热门文章
- c语言编程题:m个人围成一圈,每数n个人就踢出一个直到剩下一个人,m个人围成一圈,1,2,3循环报数,报到3的人退出,并... m个人围成一个圈有多少种方法?...
- R语言分析分析大数据的一些问题
- JAVAEE——SSH项目实战05:用户注册、登陆校验拦截器、员工拜访客户功能和MD5加密...
- 中科院自动化所副所长刘成林教授:模式识别,从初级感知到高级认知
- 谨以此文纪念逝去的SUN
- Antv | 蚂蚁数据可视化API应用
- Slider Revolution 6.X 如何设置幻灯片切换动画效果
- TCP协议的长连接和短连接详解
- 黑苹果hidp显示不清楚_可能是目前解决Mac外接显示器字体发虚的最好方法
- js创建word文档写入文本并保存为doc文件