阿里云视频点播服务流式上传返回前端进度条

该博客介绍了如何在前后端分离的项目中,利用阿里云视频点播服务的流式上传功能,并结合Redis实现视频上传进度的实时反馈。主要步骤包括:在回调方法中更新Redis中的进度数据,前端通过接口轮询获取进度。代码示例展示了控制器、业务实现层和上传进度回调方法类的实现。
摘要由CSDN通过智能技术生成

提示:前后端分离项目,流式上传到视频点播服务获取实时上传进度

阿里云视频点播服务流式上传返回前端进度条


实现思路:在阿里云的回调方法中,将上传的进度实时存入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;
    }



}


在Python中,Flask是一个轻量级的Web服务器框架,可以用于快速构建web应用。如果想要实现实时、流式的数据传输到前端,通常我们会涉及到WebSockets技术。以下是一个简单的示例,展示如何使用Flask和Flask-SocketIO来创建一个支持流式输出的应用: 首先,你需要安装Flask和相关库: ```bash pip install flask flask-socketio ``` 然后,在你的Flask应用中,设置Socket.IO服务: ```python from flask import Flask, render_template from flask_socketio import SocketIO app = Flask(__name__) app.config['SECRET_KEY'] = 'your_secret_key' socketio = SocketIO(app) # 示例函数,用于发送数据到前端 @socketio.on('connect') def handle_connection(): print('Client connected') @socketio.on('data_request') def handle_data_request(data): # 这里你可以生成实时数据 generated_data = generate_real_time_data() # 发送给所有连接的客户端 socketio.emit('new_data', generated_data, broadcast=True) if __name__ == '__main__': socketio.run(app) ``` 接下来,创建一个HTML模板(例如`templates/index.html`),引入JavaScript库如`socket.io-client`,并初始化连接: ```html <!DOCTYPE html> <html lang="en"> <head> <title>Real-time Stream</title> <script src="https://cdn.socket.io/socket.io-4.3.1.js"></script> <script src="https://code.jquery.com/jquery-3.6.0.min.js"></script> </head> <body> <button id="send-data">Send Request</button> <div id="output"></div> <script> const socket = io.connect('http://' + document.domain + ':' + location.port); $('#send-data').click(function() { socket.emit('data_request'); }); socket.on('new_data', function(data) { $('#output').append('<p>' + data + '</p>'); }); </script> </body> </html> ``` 当你点击"Send Request"按钮时,就会触发`data_request`事件,后端会响应并发送实时数据到前端,更新页面。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值