提示:前后端分离项目,流式上传到视频点播服务获取实时上传进度
阿里云视频点播服务流式上传返回前端进度条
实现思路:在阿里云的回调方法中,将上传的进度实时存入redis服务器中,然后前端在视频上传的时候重复请求获取视频上传进度的接口
话不多说:直接上代码
控制层
package com.ruiya.vod.controller;
import com.ruiya.commons.utils.R;
import com.ruiya.vod.service.VodService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
/**
* description:阿里云视频点播服务控制层
* author:maozl
* date:2022/6/16
*/
@RestController
@RequestMapping("/ability/vod")
@CrossOrigin
public class VodController {
@Autowired
private VodService vodService;
@Autowired
private RedisTemplate<String,String> redisTemplate;
/**
* 上传视频
* @param file 视频文件
* @param cateId 课程所属二级分类cateId
* @return videoId
*/
@PostMapping("/upload")
public R uploadVideo(@RequestParam("file") MultipartFile file, @RequestParam String cateId){
String videoId = vodService.uploadVideo(file,cateId);
return R.ok().message("视频上传成功").data("videoId", videoId);
}
/**
* 获取视频上传进度
* @return progressData
*/
@GetMapping ("/percent")
public R getUploadPercent(){
String progressData = redisTemplate.opsForValue().get("progressData");
return R.ok().data("progressData",progressData);
}
}
业务实现层
package com.ruiya.vod.service.impl;
import com.aliyun.vod.upload.impl.UploadVideoImpl;
import com.aliyun.vod.upload.req.UploadStreamRequest;
import com.aliyun.vod.upload.resp.UploadStreamResponse;
import com.aliyuncs.DefaultAcsClient;
import com.aliyuncs.exceptions.ClientException;
import com.aliyuncs.vod.model.v20170321.DeleteVideoRequest;
import com.ruiya.commons.handler.ServiceErrorException;
import com.ruiya.commons.utils.ResultCode;
import com.ruiya.vod.listener.PutVodProgressListener;
import com.ruiya.vod.service.VodService;
import com.ruiya.vod.utils.InitClientUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartFile;
import java.io.IOException;
import java.io.InputStream;
/**
* description:阿里云点播服务业务层
* author:maozl
* date:2022/6/16
*/
@Service
@Slf4j
public class VodServiceImpl implements VodService {
private static final String ACCESS_KEY_ID = "XXX";
private static final String ACCESS_KEY_SECRET = "XXX";
private static final String STORAGE_LOCATION = "XXX";
/**
* 注入自定义回调函数实现类
*/
@Autowired
private PutVodProgressListener putVodProgressListener;
/**
* 上传视频
* @param file
* @param cateId
* @return
*/
@Override
public String uploadVideo(MultipartFile file,String cateId) {
String videoId = null;
try {
String originalFilename = file.getOriginalFilename();
String title = originalFilename.substring(0, originalFilename.lastIndexOf("."));
//原始文件的总字节数
long totalBytes = file.getSize();
putVodProgressListener.setTotalBytes(totalBytes);
//获取文件的输入流
InputStream inputStream = file.getInputStream();
UploadStreamRequest request = new UploadStreamRequest(ACCESS_KEY_ID, ACCESS_KEY_SECRET, title, originalFilename, inputStream);
//存储区域
request.setStorageLocation(STORAGE_LOCATION);
//开启默认上传进度回调
request.setPrintProgress(true);
request.setProgressListener(putVodProgressListener);
//视频分类ID
if (!StringUtils.isEmpty(cateId)){
request.setCateId(Long.valueOf(cateId));
}
UploadVideoImpl uploader = new UploadVideoImpl();
UploadStreamResponse response = uploader.uploadStream(request);
videoId = response.getVideoId();
if (!response.isSuccess()) {
String errorMessage = "视频上传错误:" + "code:" + response.getCode() + ", message:" + response.getMessage();
log.error("上传视频出错 - {}", errorMessage);
if(StringUtils.isEmpty(videoId)){
throw new ServiceErrorException(ResultCode.ERROR, errorMessage);
}
}
log.debug("视频上传成功 - videoID:{}", videoId);
return videoId;
} catch (IOException e) {
e.printStackTrace();
log.error("上传视频失败 - {}",e);
throw new ServiceErrorException(ResultCode.ERROR, "视频点播服务上传失败");
}
}
}
上传进度回调方法类
package com.ruiya.vod.listener;
import com.aliyun.oss.event.ProgressEvent;
import com.aliyun.oss.event.ProgressEventType;
import com.aliyun.vod.upload.impl.VoDProgressListener;
import lombok.SneakyThrows;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
/**
* description:上传进度回调方法类
* author:maozl
* date:2022/6/16
*/
@Component
public class PutVodProgressListener implements VoDProgressListener {
@Autowired
private RedisTemplate<String, String> redisTemplate;
/**
* 已成功上传至OSS的字节数
*/
private long bytesWritten = 0;
/**
* 原始文件的总字节数
*/
private long totalBytes;
/**
* 本次上传成功标记
*/
private boolean succeed = false;
/**
* 视频ID
*/
private String videoId;
/**
* 图片ID
*/
private String imageId;
//构造函数中加入上传文件的总大小
public PutVodProgressListener() {
totalBytes = -1;
}
public PutVodProgressListener(long totalBytes) {
this.totalBytes = -1;
this.totalBytes = totalBytes;
System.out.println("初始化回调函数 - 待上传文件的总大小为:" + totalBytes);
}
@SneakyThrows
@Override
public void progressChanged(ProgressEvent progressEvent) {
long bytes = progressEvent.getBytes();
ProgressEventType eventType = progressEvent.getEventType();
switch (eventType) {
//开始上传事件
case TRANSFER_STARTED_EVENT:
//每次开始前,重置bytesWritten的值(不重置会导致后续上传进度超过百分之百)
this.bytesWritten = 0;
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 + ")");
//redis解决视频上传进度问题
redisTemplate.opsForValue().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 long getTotalBytes() {
return totalBytes;
}
public void setTotalBytes(long totalBytes) {
this.totalBytes = totalBytes;
}
public boolean isSucceed() {
return succeed;
}
@Override
public void onVidReady(String videoId) {
setVideoId(videoId);
}
@Override
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;
}
}