阿里云视频点播(VOD)服务的视频上传JAVA代码开发(上传)

一. 设计需求

使用阿里云视频点播服务对客户端上传的视频进行转码处理, 并存储到本地. 支持海外用户的大视频文件上传. 解决海外上传的有无问题.

二. 实现思路

客户端向服务端请求凭证, 获取上传地址, 使用阿里云sdk上传视频.

缺点:

目前美国,  澳大利亚没有节点, 这2地视频上传目前使用的是德国法兰克福的节点. 后台请求凭证接口缺少选择上传节点的模块.

后台处理需要时间, 刚刚完成上传的视频无法在中台页面播放.

三. 代码结构

1. 基础jar包. 导入点播Java SDK, 封装阿里云点播各个接口和上传所需的基础参数, 事件监听, 打成jar包, 供服务调用

2. 业务代码分层:  config, controller, service, dao, 事件消息处理, 工具类, 定时扫描作业.

四. 基础配置

1. 开通vod服务, mns服务, 并创建mns消息队列

2. 配置vod服务的转码模版组, 设置默认模板, 消息回调设置(这里使用mns)

3. 测试环境与生产环境

    默认环境配置可通过控制台或者OpenApi配置, 

    非默认环境需要调用阿里云的接口, 传appId进行配置. 部分通过OpenApi配置无效.

五. 上传相关代码

MNSListener: 监听器, 使用阿里提供的实现方案.

        ClientConfiguration clientConf = new ClientConfiguration();
        clientConf.setMaxConnections(maxConnection);
        clientConf.setIoReactorThreadCount(ioCount);
        CloudAccount account = new CloudAccount(accessKeyId, accessKeySecret, endPoint,         clientConf);
        //this client need only initialize once
        mnsClient = account.getMNSClient();
        // 开启监听线程
        try {
            Thread thread = new Thread(() -> {
                CloudQueue cloudQueue = mnsClient.getQueueRef(queue);
                while (true) {
                    Message message = null;
                    try {
                        message = cloudQueue.popMessage(15);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                    try {
                        if (message == null) {
                            //PollingWaitSeconds expire
                            //And you could do some work here or do nothing according to your business
                        } else {
                            // 使用自定义的msgHandler处理消息
                            boolean res = mnsMessageHandler.handle(message);
                            if (res) {
                                if (delMsg) {
                                    cloudQueue.deleteMessage(message.getReceiptHandle());
                                }
                            }
                        }
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            });
            thread.start();
        } catch (ClientException ce) {

UploadService: 实现阿里的上传接口. 例如文件流上传接口:

    public UploadStreamResponse uploadFile(VodUploadModule file,
                                           String extend,
                                           InputStream inputStream,
                                           String regionId,
                                           String securityToken) {
        UploadStreamRequest request = new UploadStreamRequest(accessKeyId, accessKeySecret, file.getTitle(),
                file.getFileName(), inputStream);
        request.setSecurityToken(securityToken);
        request.setApiRegionId(regionId);
        request.setCoverURL(file.getCover());
        //视频分类ID(可选)
        request.setCateId(file.getCateId());
        //视频标签,多个用逗号分隔(可选)
        request.setTitle(file.getTitle());
        if (request.getTemplateGroupId() != null) {
            request.setTemplateGroupId(file.getTemplateGroupId());
        }
        request.setTags(file.getTags());
        request.setDescription(file.getDescription());
        request.setAppId(file.getAppId());
        request.setUserData(VodRequestUserDataUtil.extendWrapper(extend));
        return videoUploadExecute(request);
    }

    private UploadStreamResponse videoUploadExecute(UploadStreamRequest request) {
        UploadVideoImpl uploader = new UploadVideoImpl();
        UploadStreamResponse response = uploader.uploadStream(request);
        if (response != null && !response.isSuccess()) {
            log.info("UploadVideoResponse ErrorCode: {} ErrorMessage: {} VideoId: {}", response.getCode(),
                    response.getMessage(), response.getVideoId());
        }
        return response;
    }

VodUploadModule: 图片/视频上传实体类: 

    private String title;
    private String tags;
    private String url;
    private String description;
    private String appId;
    private Long cateId;
    // 视频必须带后缀
    private String fileName;
    private String cover;
    /**
     * default/cover
     */
    private String imageType;
    private String businessType;
    private String fileSize;
    private String templateGroupId;
     // imageExt materialExt
    private String fileExt;

MediaExtend: 业务所需实体类. 用户上传记录.

public class MediaExtend {
    private Long userId;
    private String userType;
    private String plt;
    private String videoId;
    private String imageType;
    private String region;
    // 当需要下载到本地时, 由上传者预生成视频地址作为转码完成后的访问地址
    private String localVideoPath;
    private String localCoverPath;
    private Date createTime;
    private String extend;
}

用户请求凭证controller: 

public ServiceResponse getMaterialUploadAddress(@RequestBody FileUploadRequest info) {
        String message = null;
        try {
            MediaExtendUtil.getCurrentUserInfo(info);
            MediaExtend extend = info.getMediaExtend();
            if (!extend.validateMediaExtend()) {
                message = "Extend 不能为空";
            }
            CreateUploadAttachedMediaResponse response = uploadService.createUploadAttachedMedia(info, extend);
            return apiResponse(ResponseCode.SC_OK, message, response);
        } catch (Exception e) {
            log.error("ErrorMessage = " + e.getMessage());
            return apiResponse(ResponseCode.SC_INTERNAL_SERVER_ERROR, e.getMessage());
        }
    }

凭证service:

 public CreateUploadAttachedMediaResponse createUploadAttachedMedia(FileUploadRequest info, MediaExtend extend) throws Exception {
        CreateUploadAttachedMediaRequest request = new CreateUploadAttachedMediaRequest();
        request.setAppId(appId);
        BeanUtils.copyProperties(info, request);
        request.setMediaExt(info.getFileExt());
        DefaultAcsClient client = aliConfigService.getAcsClient(regionId);
        // extend包含了视频上传信息, 可在视频信息和转码事件中获取
request.setUserData(VodRequestUserDataUtil.extendWrapper(JSONObject.toJSONString(extend)));
        return (CreateUploadAttachedMediaResponse)client.getAcsResponse(request)        
    }

中台或App在获取到凭证后, 使用阿里云SDK上传视频. 

分享生成阿里云配置链接的代码(已删除敏感信息):

public class AliSignatureUtil {

    /*对所有参数名称和参数值做URL编码*/
    public static List<String> getAllParams(Map<String, String> publicParams, Map<String, String> privateParams) {
        List<String> encodeParams = new ArrayList<String>();
        if (publicParams != null) {
            for (Map.Entry<String, String> entry : publicParams.entrySet()) {
                //将参数和值都urlEncode一下。
                String encodeKey = percentEncode(entry.getKey());
                String encodeVal = percentEncode(entry.getValue());
                encodeParams.add(encodeKey + "=" + encodeVal);
            }
        }
        if (privateParams != null) {
            for (String key : privateParams.keySet()) {
                String value = privateParams.get(key);
                //将参数和值都urlEncode一下。
                String encodeKey = percentEncode(key);
                String encodeVal = percentEncode(value);
                encodeParams.add(encodeKey + "=" + encodeVal);
            }
        }
        return encodeParams;
    }

    /*获取 CanonicalizedQueryString*/
    public static String getCQS(List<String> allParams) {
        ParamsComparator paramsComparator = new ParamsComparator();
        Collections.sort(allParams, paramsComparator);
        StringBuffer cqString = new StringBuffer("");
        for (int i = 0; i < allParams.size(); i++) {
            cqString.append(allParams.get(i));
            if (i != allParams.size() - 1) {
                cqString.append("&");
            }
        }
        return cqString.toString();
    }

    /*字符串参数比较器,按字母序升序*/
    public static class ParamsComparator implements Comparator<String> {
        @Override
        public int compare(String lhs, String rhs) {
            return lhs.compareTo(rhs);
        }
    }

    /*构造待签名的字符串*/
    //String StringToSign = httpMethod + "&" + percentEncode("/") + "&" + percentEncode(CanonicalizedQueryString);
    /*特殊字符替换为转义字符*/
    public static String percentEncode(String value) {
        try {
            String urlEncodeOrignStr = URLEncoder.encode(value, "UTF-8");
            String plusReplaced = urlEncodeOrignStr.replace("+", "%20");
            String starReplaced = plusReplaced.replace("*", "%2A");
            String waveReplaced = starReplaced.replace("%7E", "~");
            return waveReplaced;
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        return value;
    }

    public static byte[] hmacSHA1Signature(String accessKeySecret, String stringToSign) {
        try {
            String key = accessKeySecret + "&";
            try {
                SecretKeySpec signKey = new SecretKeySpec(key.getBytes(), "HmacSHA1");
                Mac mac = Mac.getInstance("HmacSHA1");
                mac.init(signKey);
                return mac.doFinal(stringToSign.getBytes());
            } catch (Exception e) {
                throw new SignatureException("Failed to generate HMAC : " + e.getMessage());
            }
        } catch (SignatureException e) {
            e.printStackTrace();
        }
        return null;
    }

    public static String newStringByBase64(byte[] bytes)
            throws UnsupportedEncodingException {
        if (bytes == null || bytes.length == 0) {
            return null;
        }
        return new BASE64Encoder().encode(bytes);
    }

    /*生成当前UTC时间戳Time*/
    public static String generateTimestamp() {
        Date date = new Date(System.currentTimeMillis());
        SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
        df.setTimeZone(new SimpleTimeZone(0, "GMT"));
        return df.format(date);
    }

    public static String generateRandom() {
        String signatureNonce = UUID.randomUUID().toString();
        return signatureNonce;
    }

   /* public static void main(String[] args) {
        Map<String, String> publicParams = new HashMap<>();
        Map<String, String> privateParams = new HashMap<>();

        publicParams.put("Format", "json");
        publicParams.put("Version", "2017-03-21");
        publicParams.put("AccessKeyId", "");
        publicParams.put("SignatureMethod", "HMAC-SHA1");
        publicParams.put("Timestamp", generateTimestamp());
        publicParams.put("SignatureVersion", "1.0");
        publicParams.put("SignatureNonce", generateRandom());

        privateParams.put("Action", "SetMessageCallback");
        privateParams.put("CallbackType", "MNS");
        privateParams.put("MnsEndpoint", "https://111111.mns.cn-shanghai.aliyuncs.com/");
        privateParams.put("MnsQueueName", "name");
        //privateParams.put("EventTypeList", "ALL");
        privateParams.put("EventTypeList", "FileUploadComplete,ImageUploadComplete,TranscodeComplete,SnapshotComplete,UploadByURLComplete,CreateAuditComplete");
        privateParams.put("AppId", "app-1000002");

        List<String> allParams = getAllParams(publicParams, privateParams);
        String cqs = getCQS(allParams);
        System.out.println(cqs);
        String httpMethod = "GET";
        String stringToSign = httpMethod + "&" + percentEncode("/") + "&" + percentEncode(cqs);

        String signature = null;
        String domain = "http://vod.eu-central-1.aliyuncs.com/";
        try {
            signature = newStringByBase64(hmacSHA1Signature("", stringToSign));
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        String url = domain + "?" + cqs + "&Signature=" + signature;
        System.out.println(url);
    }*/


    /*public static void main(String[] args) {
        Map<String, String> publicParams = new HashMap<>();
        Map<String, String> privateParams = new HashMap<>();

        publicParams.put("Format", "json");
        publicParams.put("Version", "2017-03-21");
        publicParams.put("AccessKeyId", "");
        publicParams.put("SignatureMethod", "HMAC-SHA1");
        publicParams.put("Timestamp", generateTimestamp());
        publicParams.put("SignatureVersion", "1.0");
        publicParams.put("SignatureNonce", generateRandom());

        privateParams.put("Action", "SetMessageCallback");
        privateParams.put("CallbackType", "MNS");
        privateParams.put("MnsEndpoint", "https://111111.mns.cn-shanghai.aliyuncs.com/");
        privateParams.put("MnsQueueName", "name");
        //privateParams.put("EventTypeList", "ALL");
        privateParams.put("EventTypeList", "FileUploadComplete,ImageUploadComplete,TranscodeComplete,SnapshotComplete,UploadByURLComplete,CreateAuditComplete");



        privateParams.put("AppId", "app-1000002");

        List<String> allParams = getAllParams(publicParams, privateParams);
        String cqs = getCQS(allParams);
        System.out.println(cqs);
        String httpMethod = "GET";
        String stringToSign = httpMethod + "&" + percentEncode("/") + "&" + percentEncode(cqs);

        String signature = null;
        String domain = "http://vod.eu-central-1.aliyuncs.com/";
        try {
            signature = newStringByBase64(hmacSHA1Signature("", stringToSign));
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        String url = domain + "?" + cqs + "&Signature=" + signature;
        System.out.println(url);
    }*/


    /**
     * 添加转码模板组配置
     */
    public static AddTranscodeTemplateGroupResponse addTranscodeTemplateGroup(DefaultAcsClient client) throws Exception {
        AddTranscodeTemplateGroupRequest request = new AddTranscodeTemplateGroupRequest();
        //转码模板ID
        request.setName("video-transcode-LD-1");
        request.setAppId("app-1000002");
        System.out.println(buildTranscodeTemplateList().toJSONString());
        request.setTranscodeTemplateList(buildTranscodeTemplateList().toJSONString());
        return client.getAcsResponse(request);
    }

    /**
     * 构建需要添加的转码模板配置数据
     *
     * @return
     */
    public static JSONArray buildTranscodeTemplateList() {
        JSONObject transcodeTemplate = new JSONObject();
        //模板名称
        transcodeTemplate.put("TemplateName", "video-transcode-LD-1");
        //清晰度
        transcodeTemplate.put("Definition", "SD");
        //视频流转码配置
        JSONObject video = new JSONObject();
        // 文档地址https://help.aliyun.com/document_detail/52839.html?spm=a2c4g.11186623.2.16.6bb96d22lCXeg4#h2--transcodetemplategroup-div-id-transcodetemplategroup-div-36
        // 转码视频流配置Video
        //分辨率(宽x高)
        video.put("Width", 960);
        //码率(Kbps)
        video.put("Bitrate", 900);
        //帧率(fps)
        video.put("Fps", 25);
        video.put("Remove", false);
        //编码格式
        video.put("Codec", "H.264");
        // 关键帧最大间隔(帧)
        video.put("Gop", "250");
        transcodeTemplate.put("Video", video);
        //音频流转码配置
        JSONObject audio = new JSONObject();
        // 编码格式
        audio.put("Codec", "AAC");
        // 码率(Kbps)
        audio.put("Bitrate", "96");
        // 声道数
        audio.put("Channels", "2");
        // 采样率
        audio.put("Samplerate", "44100");
        transcodeTemplate.put("Audio", audio);
        // 封装容器
        JSONObject container = new JSONObject();
        // 封装格式
        container.put("Format", "mp4");
        transcodeTemplate.put("Container", container);
        //条件转码配置
        JSONObject transconfig = new JSONObject();
        transconfig.put("IsCheckReso", false);
        transconfig.put("IsCheckResoFail", false);
        transconfig.put("IsCheckVideoBitrate", false);
        transconfig.put("IsCheckVideoBitrateFail", false);
        transconfig.put("IsCheckAudioBitrate", false);
        transconfig.put("IsCheckAudioBitrateFail", false);
        transcodeTemplate.put("TransConfig", transconfig);
        //加密配置(只支持m3u8)
        //JSONObject encryptSetting = new JSONObject();
        //encryptSetting.put("EncryptType", "Private");
        //transcodeTemplate.put("EncryptSetting", encryptSetting);
        //水印ID(多水印关联)
       /* JSONArray watermarkIdList = new JSONArray();
        watermarkIdList.add("263261bdc1ff65782f8995c6dd22a16a");
        //USER_DEFAULT_WATERMARK 代表默认水印ID
        watermarkIdList.add("USER_DEFAULT_WATERMARK");
        transcodeTemplate.put("WatermarkIds", watermarkIdList);*/
        JSONArray transcodeTemplateList = new JSONArray();
        transcodeTemplateList.add(transcodeTemplate);
        return transcodeTemplateList;
    }
    /*  */

    /**
     * 以下为调用示例
     */
  /* public static void main(String[] args) throws ClientException {
        DefaultAcsClient client = initVodClient("", "");
        AddTranscodeTemplateGroupResponse response = new AddTranscodeTemplateGroupResponse();
        try {
            response = addTranscodeTemplateGroup(client);
            System.out.println("TranscodeTemplateGroupId = " + response.getTranscodeTemplateGroupId());
        } catch (Exception e) {
            System.out.println("ErrorMessage = " + e.getLocalizedMessage());
        }
        System.out.println("RequestId = " + response.getRequestId());
    }*/

    public static DefaultAcsClient initVodClient(String accessKeyId, String accessKeySecret) throws ClientException {
       /* String regionId = "cn-shanghai";  // 点播服务接入区域
        
        String regionId2 = "ap-southeast-1";
      
        String regionId3 = "ap-southeast-5";
        
        String regionId4 = "ap-south-1";*/
        
        String regionId5 = "eu-central-1";
        
        //String regionId6 = "ap-northeast-1";
        
        DefaultProfile profile = DefaultProfile.getProfile(regionId5, accessKeyId, accessKeySecret);
        DefaultAcsClient client = new DefaultAcsClient(profile);
        return client;
    }

    public static ListTranscodeTemplateGroupResponse listTranscodeTemplateGroup(DefaultAcsClient client) throws Exception {
        ListTranscodeTemplateGroupRequest request = new ListTranscodeTemplateGroupRequest();
        request.setAppId("app-1000002");
        return client.getAcsResponse(request);
    }
    /**
     * 以下为调用示例
     */
    /*public static void main(String[] args) throws ClientException {
        DefaultAcsClient client = initVodClient("", "");
        ListTranscodeTemplateGroupResponse response = new ListTranscodeTemplateGroupResponse();
        try {
            response = listTranscodeTemplateGroup(client);
            System.out.println(JSONObject.toJSONString(response.getTranscodeTemplateGroupList()));
        } catch (Exception e) {
            System.out.println("ErrorMessage = " + e.getLocalizedMessage());
        }
        System.out.println("RequestId = " + response.getRequestId());
    }*/

    /**
     * 设置默认转码模板组
     */
    public static SetDefaultTranscodeTemplateGroupResponse setDefaultTranscodeTemplateGroup(DefaultAcsClient client) throws Exception {
        SetDefaultTranscodeTemplateGroupRequest request = new SetDefaultTranscodeTemplateGroupRequest();
        request.setTranscodeTemplateGroupId("111111111111111111");
        return client.getAcsResponse(request);
    }
    /**
     * 以下为调用示例
     */
    /*public static void main(String[] args) throws ClientException {
        DefaultAcsClient client = initVodClient("", "");
        SetDefaultTranscodeTemplateGroupResponse response = new SetDefaultTranscodeTemplateGroupResponse();
        try {
            response = setDefaultTranscodeTemplateGroup(client);
        } catch (Exception e) {
            System.out.println("ErrorMessage = " + e.getLocalizedMessage());
        }
        System.out.println("RequestId = " + response.getRequestId());
    }*/
}

 

  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值