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的解决方案)补充:前后端分离项目中获取进度解决方案相关推荐

  1. springboot前后端分离项目MultipartFile获取前端传的file为null问题

    [问题描述] springboot+vue 前后端分离项目,在对接上传文件功能时,一直显示文件为空. [分析] 查找了很多博客,解决方法五花八门,但原因应该还是 springboot自带的org.sp ...

  2. 阿里云视频点播上传视频和获取视频播放地址

    参考官方文档: Python上传SDK-普通文件上传.获取播放地址 本文只对上传视频和获取单个视频播放地址操作.更多案例如批量获取所有视频列表等操作请直接参考官方文档: 媒体管理 一.上传视频:上传完 ...

  3. 阿里云SDK上传视频

    1.老样子,先看效果图 2.首先到阿里云下载所需要用到的SDK 3.下载好的,解压之后,目录以及运行起来是以下这个样子的 4.在实际项目中引用,先将SDK添加到项目中,放到public目录下 5.在p ...

  4. 使用阿里云视频点播上传视频

    目录 前言 一.目的 二.实现步骤 1.准备操作 2.主要使用模块 2.1. 上传音频 2.2. 添加转码设置 3.搭建视频点播环境 3.1.配置Maven仓库 3.2.添加jar依赖 3.3.上传视 ...

  5. 【springboot】实现阿里云视频点播上传视频

    阿里云视频点播 1.开通视频点播服务,选择按流量计费,对于个人项目来说,相当于免费. 2.开启存储管理 3.设置转码模板组.设置加密,如果不加密,别人只要获取到你视频的url就可以免费观看,但进行了加 ...

  6. 小程序中使用阿里云oss上传视频怎么获得视频截图?

    在视频地址后面加上如下的后缀,就可以获取视频内部帧图片,当作封面使用. ?x-oss-process=video/snapshot,t_1000,m_fast ?x-oss-process=video ...

  7. spring boot 整合 阿里云oss上传

    Spring Boot 整合 阿里云OSS上传 OSS申请和配置 1. 注册登录 2.开通以及配置 springboot整合使用 1. 进入我们springboot的项目中,导入oss相关依赖 2. ...

  8. 前后端分离项目,前端cookie获取不到的解决方案

    最近在写前后端分离项目中发现,后端在response中添加cookie后 response.addCookie(cookie) 前端却无法拿到cookie,但是在网络抓包中确实存在cookie 但是在 ...

  9. web利用腾讯云点播上传视频

    web利用腾讯云点播上传视频到云服务器 第一步导入 <script src="//imgcache.qq.com/open/qcloud/js/vod/sdk/ugcUploader. ...

最新文章

  1. C++中 int char 的相互转换
  2. C++Tower of Hanoi汉诺塔的实现算法(附完整源码)
  3. centos 安装php扩展gd,linux(centos)下为php添加添加GD扩展
  4. 税收分类编码2020_增值税开票系统你会吗?2020最新开票(金税盘版)图文教程详细版...
  5. Spring Boot文档阅读笔记-构建Restful风格的WebService
  6. PWDX查找程序执行路径
  7. 四年磨一剑:我是如何拿到蚂蚁 Offer 的?
  8. 已解决:录屏软件录不了全屏的问题
  9. Redis | 哨兵模式-Sentinel
  10. IDEA开发Springboot时,远程使用企业内部不能连接数据库的解决方法
  11. html自动补位的功能,js中位数不足自动补位扩展padLeft、padRight实现代码
  12. 找出1000瓶牛奶中有毒的牛奶
  13. 卸载百度软件修复服务器,快速修复win7系统笔记本卸载百度软件中心助手的解决教程...
  14. [转]中英文停止词表(stopword)
  15. 异贝,移动互联网技术,为中小微实体企业联盟、线上链接、线上线下自定义营销方案推送。案例42
  16. 【哈希表】(一) 设计哈希表
  17. 合肥通用职业技术学院计算机专业,2020年安徽高考专科提前批及大专录取时间及录取结果查询...
  18. 爬去微信小程序服务器代码,Python爬取微信小程序通用方法代码实例详解
  19. 海康威视人脸识别门禁系统对接
  20. 为什么说创意是一部宣传片的灵魂

热门文章

  1. c语言编程题:m个人围成一圈,每数n个人就踢出一个直到剩下一个人,m个人围成一圈,1,2,3循环报数,报到3的人退出,并... m个人围成一个圈有多少种方法?...
  2. R语言分析分析大数据的一些问题
  3. JAVAEE——SSH项目实战05:用户注册、登陆校验拦截器、员工拜访客户功能和MD5加密...
  4. 中科院自动化所副所长刘成林教授:模式识别,从初级感知到高级认知
  5. 谨以此文纪念逝去的SUN
  6. Antv | 蚂蚁数据可视化API应用
  7. Slider Revolution 6.X 如何设置幻灯片切换动画效果
  8. TCP协议的长连接和短连接详解
  9. 黑苹果hidp显示不清楚_可能是目前解决Mac外接显示器字体发虚的最好方法
  10. js创建word文档写入文本并保存为doc文件