基于 oss的视频点播

视频加密 与授权播放

上传的视频 需要加密,保证数据安全, 并且这个数据只允许部分用户访问。

点击开通视频点播服务,然后 进入控制台管理

点击 媒体处理配置, 设置封装格式为 hls ,然后设置 加密

参考服务端 API

视频点播服务 官网文档

参考服务端文档

官方给的 demo下载页面

参考网上的一段代码

先去调用后台的 api 接口,后台返回一个 token凭证,前端用户根据这个凭证 才能够播放【相当于 由我后台服务手动确认并且授权】

1
2
3
4
5
6
{
    "data":{
        success: true,
        content: "这个是凭证"
    }
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
playVod (vod) {
        let _this = this;
        Loading.show();
        _this.$ajax.get(process.env.VUE_APP_SERVER + '/file/admin/get-auth/' + vod).then((response)=>{
          Loading.hide();
          let resp = response.data;
          if (resp.success) {
            //如果已经有播放器了,则将播放器div删除
            if (_this.aliPlayer) {
              _this.aliPlayer = null;
              $("#" + _this.playerId + '-player').remove();
            }

            // 初始化播放器
            $("#" + _this.playerId).append("<div class=\"prism-player\" id=\"" + _this.playerId + "-player\"></div>");
            _this.aliPlayer = new Aliplayer({
              id: _this.playerId + '-player',
              width: '100%',
              autoplay: false,
              vid: vod,
              playauth: resp.content,
              cover: 'http://liveroom-img.oss-cn-qingdao.aliyuncs.com/logo.png',
              encryptType:1, //当播放私有加密流时需要设置。
            },function(player){
              console.log('播放器创建好了。')
            });
          } else {
            Toast.warning('播放错误。')
          }
        })

path 和 vodId

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
@GetMapping("/check/{key}")
    public ResponseDto check(@PathVariable String key) throws Exception {
        LOG.info("检查上传分片开始:{}", key);
        ResponseDto responseDto = new ResponseDto();
        FileDto fileDto = fileService.findByKey(key);
        if (fileDto != null) {
            if (StringUtils.isEmpty(fileDto.getVod())) {
                //如果 没有 vod,说明是 上传到了 OSS 里面
                fileDto.setPath(OSS_DOMAIN + fileDto.getPath());
            } else {
                //如果有 vod ,说明是上传到 点播服务里面, 要通过 vodUtil 获取到视频的原地址
                DefaultAcsClient vodClient = VodUtil.initVodClient(accessKeyId, accessKeySecret);
                GetMezzanineInfoResponse response = VodUtil.getMezzanineInfo(vodClient, fileDto.getVod());
                System.out.println("获取视频信息, response : " + JSON.toJSONString(response));
                String fileUrl = response.getMezzanine().getFileURL();
                fileDto.setPath(fileUrl);
            }
        }
        responseDto.setContent(fileDto);
        return responseDto;
    }

https://help.aliyun.com/document_detail/53406.html

https://help-static-aliyun-doc.aliyuncs.com/assets/img/zh-CN/1740775061/p183800.png

https://help.aliyun.com/document_detail/99889.html

这里面 使用 服务器上传 不是很好,建议 使用 客户端上传,通过应用服务器鉴权,然后再上传。

https://help.aliyun.com/document_detail/55407.htm?spm=a2c4g.11186623.0.0.45542e98dRS1Mw#doc-api-vod-CreateUploadVideo

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
var uploader = new AliyunUpload.Vod({
       //userID,必填,只需有值即可。
       userId:"122",
       //分片大小默认1 MB,不能小于100 KB
       partSize: 1048576,
     //并行上传分片个数,默认5
       parallel: 5,
     //网络原因失败时,重新上传次数,默认为3
     retryCount: 3,
     //网络原因失败时,重新上传间隔时间,默认为2秒
     retryDuration: 2,
     //是否上报上传日志到视频点播,默认为true
     enableUploadProgress: true,
      //开始上传
      'onUploadstarted': function (uploadInfo) {
        log("onUploadStarted:" + uploadInfo.file.name + ", endpoint:" + uploadInfo.endpoint + ", bucket:" + uploadInfo.bucket + ", object:" + uploadInfo.object);
    //上传方式1,需要根据uploadInfo.videoId是否有值,调用视频点播的不同接口获取uploadauth和uploadAddress,如果videoId有值,调用刷新视频上传凭证接口,否则调用创建视频上传凭证接口
    if (uploadInfo.videoId) {
            //如果uploadInfo.videoId存在,调用刷新视频上传凭证接口
         }
     else{
            //如果uploadInfo.videoId不存在,调用获取视频上传地址和凭证接口
      //从视频点播服务获取的uploadAuth、uploadAddress和videoId,设置到SDK里
         uploader.setUploadAuthAndAddress(uploadInfo, uploadAuth, uploadAddress,videoId);
         }
      },
      //文件上传成功
      'onUploadSucceed': function (uploadInfo) {
        log("onUploadSucceed: " + uploadInfo.file.name + ", endpoint:" + uploadInfo.endpoint + ", bucket:" + uploadInfo.bucket + ", object:" + uploadInfo.object);
      },
      //文件上传失败
      'onUploadFailed': function (uploadInfo, code, message) {
        log("onUploadFailed: file:" + uploadInfo.file.name + ",code:" + code + ", message:" + message);
      },
      //文件上传进度,单位:字节
      'onUploadProgress': function (uploadInfo, totalSize, loadedPercent) {
          log("onUploadProgress:file:" + uploadInfo.file.name + ", fileSize:" + totalSize + ", percent:" + Math.ceil(loadedPercent * 100) + "%");
      },
      //上传凭证超时
      'onUploadTokenExpired': function (uploadInfo) {
          console.log("onUploadTokenExpired");
      //实现时,根据uploadInfo.videoId调用刷新视频上传凭证接口重新获取UploadAuth
      //从点播服务刷新的uploadAuth,设置到SDK里

      uploader.resumeUploadWithAuth(uploadAuth);
      },
    //全部文件上传结束
    'onUploadEnd':function(uploadInfo){
           console.log("onUploadEnd: uploaded all the files");
       }
});

这里最好的一个方法就是 前端,客户端上传,去调用后端 的api 获取视频凭证。

上传之后再通知后端 是比较好的。

我自己写的代码,成功了

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
  @Autowired
    private AliYunOssPropertis prop;
    @Autowired
    private AliyunOssFileServiceImpl aliyunOssFileService;

    @Test
    void testStsToken() {
        String accessKeyId = prop.getAccessKey();
        String accessKeySecret = prop.getAccessKeySecret();
        //需要在RAM控制台获取,此时要给子账号权限,并建立一个角色,把这个角色赋给子账户,这个角色会有一串值,就是rolearn要填的
        //记得角色的权限,子账户的权限要分配好,不然会报错
        String roleArn =  prop.getRoleArn();
                /*"acs🐏:1889680656740228:role/test0";*/
        // 临时Token的会话名称,自己指定用于标识你的用户,主要用于审计,或者用于区分Token颁发给谁 
        String roleSessionName = prop.getSessionName();
                // "test0@role.1889680656740228.onaliyunservice.com";
        //这个可以为空,不好写,格式要对,无要求建议为空
        String policy = null;
        ProtocolType protocolType = ProtocolType.HTTPS;
        try {
            AssumeRoleResponse response = aliyunOssFileService.assumeRole(accessKeyId, accessKeySecret, roleArn,
                    roleSessionName, policy, protocolType);
            //这个是临时的 appkeyID
            String accesskeyid = response.getCredentials().getAccessKeyId();
            // 临时的 secret
            String accesskeysecret = response.getCredentials().getAccessKeySecret();
            //   临时的上传 token        
            String securitytoken = response.getCredentials().getSecurityToken();
            // System.out.println(securitytoken);
            // System.out.println(securitytoken);
            System.out.println(String.format("keyid=%s,  secret= %s, sts token = %s",accesskeyid,accessKeySecret,securitytoken));
        } catch (ClientException | com.aliyuncs.exceptions.ClientException e) {
            e.printStackTrace();
        }
    }
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
package io.github.lyr2000.dissertation.service.impl;

import cn.hutool.core.util.IdUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.aliyun.oss.ClientException;
import com.aliyun.oss.OSS;
import com.aliyun.oss.OSSClient;
import com.aliyun.oss.common.utils.BinaryUtil;
import com.aliyun.oss.model.MatchMode;
import com.aliyun.oss.model.PolicyConditions;
import com.aliyuncs.DefaultAcsClient;
import com.aliyuncs.auth.sts.AssumeRoleRequest;
import com.aliyuncs.auth.sts.AssumeRoleResponse;
import com.aliyuncs.http.MethodType;
import com.aliyuncs.http.ProtocolType;
import com.aliyuncs.profile.DefaultProfile;
import com.aliyuncs.profile.IClientProfile;
import io.github.lyr2000.common.dto.Result;
import io.github.lyr2000.dissertation.components.AliYunOssPropertis;
import io.github.lyr2000.dissertation.pojo.dto.OssCallbackParam;
import io.github.lyr2000.dissertation.pojo.dto.ResultBean;
import io.github.lyr2000.dissertation.service.FileService;
import io.github.lyr2000.dissertation.util.ResultUtil;
import lombok.Data;
import lombok.ToString;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Profile;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Map;

/**
 * @author lyr
 * @description 阿里OSS 文件
 * @create 2021-11-19 15:39
 */
@Slf4j
@Service
@ToString
@Data
public class AliyunOssFileServiceImpl implements FileService {

    // @Value("${aliyun.oss.accessKey}")
    // private String accessKey;
    //
    // @Value("${aliyun.oss.accessKeySecret}")
    // private String accessKeySecret;
    @Resource
    private AliYunOssPropertis propertis;

    @Override
    public Result<?> uploadFile(MultipartFile file, String originLocalFileName, String uploadUID,
                                int partCnt, int totalCnt) {
        return null;
    }

    @Override
    public Result<?> uploadVideo(MultipartFile file) {
        return null;
    }


    @Value("${aliyun.oss.policy.expire}")
    private int ALIYUN_OSS_EXPIRE;
    @Value("${aliyun.oss.maxSize}")
    private int ALIYUN_OSS_MAX_SIZE;
    @Value("${aliyun.oss.callback}")
    private String ALIYUN_OSS_CALLBACK;
    @Value("${aliyun.oss.bucketName}")
    private String ALIYUN_OSS_BUCKET_NAME;
    @Value("${aliyun.oss.endpoint}")
    private String ALIYUN_OSS_ENDPOINT;
    @Value("${aliyun.oss.dir.prefix}")
    private String ALIYUN_OSS_DIR_PREFIX;

    @PostConstruct
    @Profile({"test","tests"})
    void printProfiles() {
        log.info("aliyun profiles = {}",this.toString());
    }

    /**
     * //  * 生成前端直传 token
     * //  * @return
     * //
     */
    // @Override
    // public String generateUploadToken() {
    //     return null;
    // }
    @Resource
    private OSSClient ossClient;

    @Override
    public Result<?> getPolicy() {
        JSONObject resultBean = new JSONObject();
        // 存储目录
        String dir = ALIYUN_OSS_DIR_PREFIX  + "/";
        // 签名有效期
        long expireEndTime = System.currentTimeMillis() + ALIYUN_OSS_EXPIRE * 1000;
        Date expiration = new Date(expireEndTime);
        // 文件大小: 最大 2M, 图片文件
        long maxSize = 2 * 1024 * 1024;
        // 回调
        OssCallbackParam callback = new OssCallbackParam();
        callback.setCallbackUrl(ALIYUN_OSS_CALLBACK);
        callback.setCallbackBody("filename=${object}&size=${size}&mimeType=${mimeType}&height=${imageInfo.height}&width=${imageInfo.width}");
        callback.setCallbackBodyType("application/x-www-form-urlencoded");
        // 提交节点
        String action = "https://"+propertis.getEndpoint();
        /*"https://" + ALIYUN_OSS_ENDPOINT;*/
        try {
            PolicyConditions policyConds = new PolicyConditions();
            //设置文件最大大小
            policyConds.addConditionItem(PolicyConditions.COND_CONTENT_LENGTH_RANGE, 0, maxSize);
            //设置上传目录
            policyConds.addConditionItem(MatchMode.StartWith, PolicyConditions.COND_KEY, dir);
            String postPolicy = ossClient.generatePostPolicy(expiration, policyConds);
            byte[] binaryData = postPolicy.getBytes("utf-8");
            String policy = BinaryUtil.toBase64String(binaryData);
            String signature = ossClient.calculatePostSignature(postPolicy);
            String callbackData = BinaryUtil.toBase64String(JSONUtil.parse(callback).toString().getBytes("utf-8"));
            resultBean.put("accessid", ossClient.getCredentialsProvider().getCredentials().getAccessKeyId());
            resultBean.put("policy", policy);
            resultBean.put("signature", signature);
            resultBean.put("dir", dir);
            resultBean.put("expire", String.valueOf(expireEndTime / 1000));
            resultBean.put("callback", callbackData);
            resultBean.put("host", action);
            // 返回结果
        } catch (Exception e) {
            log.error("签名生成失败", e);
            return ResultUtil.fail();
        }
        String fileName = IdUtil.fastSimpleUUID();
        // return new ResultBean(ResultBean.RESCODE_SUCCESS, ResultBean.RESMSG_SUCCESS, resultBean);
        return ResultUtil.mapOf("policy",resultBean,"fileName",fileName);
    }

    @Override
    public void   callback(HttpServletRequest request, HttpServletResponse resp) {
        //verify =====
        // String pubKeyUrl = request.getHeader("x-oss-pub-key-url");



        JSONObject resultBean = new JSONObject();

        String filename = request.getParameter("filename");

        filename = "https://".concat(ALIYUN_OSS_ENDPOINT).concat("/").concat(filename);

        resultBean.put("filename", filename);
        resultBean.put("size", request.getParameter("size"));
        resultBean.put("mimeType", request.getParameter("mimeType"));
        resultBean.put("width", request.getParameter("width"));
        resultBean.put("height", request.getParameter("height"));

        // return new ResultBean(ResultBean.RESCODE_SUCCESS, ResultBean.RESMSG_SUCCESS, resultBean);
        Result<Map<Object, Object>> res = ResultUtil.mapOf("res", resultBean);
        // resp.getWriter().println();
        try {
            resp.setStatus(HttpServletResponse.SC_OK);
            // resp.setStatus(HttpServletResponse.SC_BAD_REQUEST); - 设置失败
            ResultUtil.writeJSON(resp.getWriter(),res);
        } catch (IOException e) {
            log.error("io error = {}",e.getMessage());
        }
        // return null;
    }

    @Override
    public Result<?> generateUploadVideoToken(String videoName, String userId) {
        return null;
    }
    // // @Override
    // // public Result<?> uploadFile(MultipartFile file, int sliceIndex, int totalCnt, String originName) {
    // //      return null;
    // // }
    //
    // public AssumeRoleResponse assumeRole(String roleSessionName) throws ClientException {
    //     // 创建一个 Aliyun Acs Client, 用于发起 OpenAPI 请求
    //     // 只有 RAM用户(子账号)才能调用 AssumeRole 接口
    //     // 阿里云主账号的AccessKeys不能用于发起AssumeRole请求
    //     // 请首先在RAM控制台创建一个RAM用户,并为这个用户创建AccessKeys
    //     IClientProfile profile = DefaultProfile.getProfile(aliyunOssREGION_CN_HANGZHOU, aliyunOssAccessKeyID,
    //             aliyunOssAccessKeySecret);
    //     DefaultAcsClient client = new DefaultAcsClient(profile);
    //     // 创建一个 AssumeRoleRequest 并设置请求参数
    //     final AssumeRoleRequest request = new AssumeRoleRequest();
    //     request.setVersion("1");
    //     request.setMethod(MethodType.POST);
    //     // 此处必须为 HTTPS
    //     request.setProtocol(ProtocolType.HTTPS);
    //     // RoleArn 需要在 RAM 控制台上获取
    //     // request.setRoleArn(aliyunOssRoleArn);
    //     // RoleSessionName 是临时Token的会话名称,自己指定用于标识你的用户,主要用于审计,或者用于区分Token颁发给谁
    //     // 但是注意RoleSessionName的长度和规则,不要有空格,只能有'-' '_' 字母和数字等字符
    //     // 具体规则请参考API文档中的格式要求
    //     request.setRoleSessionName(roleSessionName);
    //     // 授权策略
    //     request.setPolicy(readJson(aliyunOssPolicyFile));
    //     // 设置token时间
    //     request.setDurationSeconds(aliyunOssTokenExpireTime * 60L);
    //     // 发起请求,并得到response
    //     return client.getAcsResponse(request);
    // }


    // 目前只有"cn-hangzhou"这个region可用, 不要使用填写其他region的值
    public static final String REGION_CN_HANGZHOU = "cn-hangzhou";
    // 当前 STS API 版本
    public static final String STS_API_VERSION = "2015-04-01";


    //静态方法,方便调用
    public static AssumeRoleResponse assumeRole(String accessKeyId, String accessKeySecret,
                                                String roleArn, String roleSessionName, String policy,
                                                ProtocolType protocolType) throws ClientException, com.aliyuncs.exceptions.ClientException {
        try {
            // 创建一个 Aliyun Acs Client, 用于发起 OpenAPI 请求
            IClientProfile profile = DefaultProfile.getProfile(REGION_CN_HANGZHOU, accessKeyId, accessKeySecret);
            DefaultAcsClient client = new DefaultAcsClient(profile);

            // 创建一个 AssumeRoleRequest 并设置请求参数
            final AssumeRoleRequest request = new AssumeRoleRequest();
            request.setVersion(STS_API_VERSION);
            request.setMethod(MethodType.POST);
            request.setProtocol(protocolType);

            request.setRoleArn(roleArn);
            request.setRoleSessionName(roleSessionName);
            request.setPolicy(policy);

            // 发起请求,并得到response
            final AssumeRoleResponse response = client.getAcsResponse(request);

            return response;
        } catch (ClientException | com.aliyuncs.exceptions.ClientException e) {
            throw e;
        }
    }

    //获取 sts上传 TOKEN
    @Override
    public Result<Map<Object, Object>> getStsToken() {
        String roleArn = propertis.getRoleArn();
        String sessionName = propertis.getSessionName();
        String endpoint = propertis.getEndpoint();
        String bucketName = propertis.getBucketName();
        String accessKey = propertis.getAccessKey();
        String accessKeySecret = propertis.getAccessKeySecret();
        try {
            AssumeRoleResponse res = assumeRole(accessKey, accessKeySecret, roleArn, sessionName, null, ProtocolType.HTTPS);
            AssumeRoleResponse.Credentials c = res.getCredentials();
            return ResultUtil.mapOf("appid",c.getAccessKeyId(),"secret",c.getAccessKeySecret(),"token",c.getSecurityToken());
        } catch (com.aliyuncs.exceptions.ClientException e) {
            log.error("err := {}",e.toString());
            return null;
        }
        // return null;
    }

}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
package io.github.lyr2000.dissertation.controller.openapi;

// import io.github.lyr2000.dissertation.components.OssAuthcUtil;

import com.aliyun.oss.OSS;
import com.aliyuncs.DefaultAcsClient;
import com.aliyuncs.auth.sts.AssumeRoleRequest;
import com.aliyuncs.exceptions.ClientException;
import com.aliyuncs.http.MethodType;
import com.aliyuncs.http.ProtocolType;
import com.aliyuncs.profile.DefaultProfile;
import com.aliyuncs.profile.IClientProfile;
import io.github.lyr2000.common.dto.Result;
import io.github.lyr2000.dissertation.components.AliYunOssPropertis;
import io.github.lyr2000.dissertation.config.OssConfig;
import io.github.lyr2000.dissertation.pojo.dto.ResultBean;
import io.github.lyr2000.dissertation.service.FileService;
import io.swagger.annotations.Api;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Map;

/**
 * @author lyr
 * @description oss鉴权
 * @create 2021-11-20 14:42
 */
@Slf4j
@Api(tags = "OSS 上传授权接口")
@RestController
@CrossOrigin(origins = "http://localhost:3000", maxAge = 3600)
@RequestMapping("/free/")
public class OssController {
    @Resource
    private FileService ossService;

 

    @Autowired
    private OSS oss;
    @Autowired
    private AliYunOssPropertis propertis;


    @PostMapping("/sts")
    public Result<?> getStsToken() throws ClientException {
         return ossService.getStsToken();
    }
}
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>阿里云 JavaScript上传SDK Demo (使用jquery) STSToken 上传方式 </title>
    <script src="/lib/jquery.min.js"></script>
    <script src="/lib/aliyun-upload-sdk/aliyun-upload-sdk-1.5.2.min.js"></script>
    <script src="/lib/aliyun-upload-sdk/lib/es6-promise.min.js"></script>
    <script src="/lib/aliyun-upload-sdk/lib/aliyun-oss-sdk-6.13.0.min.js"></script>
    <!-- <script type="text/javascript" src="./lib/aliyun-upload-sdk.js"></script> -->
    <style type="text/css">
        .container {
            width: 1200px;
            margin: 0 auto;
        }

        .input-control {
            margin: 5px 0;
        }

        .input-control label {
            font-size: 14px;
            color: #333;
            width: 30%;
            text-align: right;
            display: inline-block;
            vertical-align: middle;
            margin-right: 10px;
        }

        .input-control input {
            width: 30%;
            height: 30px;
            padding: 0 5px;
        }

        .upload {
            padding: 30px 50px;
        }

        .progress {
            font-size: 14px;
        }

        .progress i {
            font-style: normal;
        }

        .upload-type {
            color: #666;
            font-size: 12px;
            padding: 10px 0;
        }

        .upload-type button {
            margin: 0 10px 0 20px;
        }

        .status {
            font-size: 14px;
            margin-left: 30px;
        }

        .info {
            font-size: 14px;
            padding-left: 30px;
        }
    </style>
</head>
<body>
<div class="container">
    <div class="setting">
        <div class="input-control">
            <label for="timeout">请求过期时间(配置项 timeout, 默认 60000):</label>
            <input type="text" id="timeout" placeholder="输入过期时间, 单位毫秒">
        </div>

        <div class="input-control">
            <label for="partSize">分片大小(配置项 partSize, 默认 1048576):</label>
            <input type="text" class="form-control" id="partSize" placeholder="输入分片大小, 单位bit, 最小100k">
        </div>

        <div class="input-control">
            <label for="parallel">上传分片数(配置项 parallel, 默认 5):</label>
            <input type="text" class="form-control" id="parallel" placeholder="输入并行上传分片个数, 默认为5">
        </div>

        <div class="input-control">
            <label for="retryCount">网络失败重试次数(配置项 retryCount, 默认 3):</label>
            <input type="text" class="form-control" id="retryCount" placeholder="输入网络失败重试次数, 默认为3">
        </div>

        <div class="input-control">
            <label for="retryDuration">网络失败重试间隔(配置项 retryDuration, 默认 2):</label>
            <input type="text" class="form-control" id="retryDuration" placeholder="输入网络失败重试间隔, 默认2秒">
        </div>

        <div class="input-control">
            <label for="region">配置项 region, 默认 cn-shanghai:</label>
            <select id="region">
                <option>cn-shanghai</option>
                <option>eu-central-1</option>
                <option>ap-southeast-1</option>
            </select>
        </div>

        <div class="input-control">
            <label for="userId">阿里云账号ID:</label>
            <input type="text" value="1303984639806000" disabled class="form-control" id="userId"
                   placeholder="输入阿里云账号ID">
            userId必填,只需要有值即可
        </div>

    </div>

    <div class="upload">
        <div>
            <input type="file" id="fileUpload">
            <label class="status">上传状态: <span id="status"></span></label>
        </div>
        <div class="upload-type">
            上传方式二, 使用 STSToken 上传:
            <button id="stsUpload" disabled="false">开始上传</button>
            <button id="pauseUpload">暂停</button>
            <button id="resumeUpload" disabled="false">恢复上传</button>
            <span class="progress">上传进度: <i id="sts-progress">0</i> %</span>
        </div>
    </div>
    <div class="info">点播STS参数如何获取,请查阅<a href="https://help.aliyun.com/document_detail/57114.html" target="_blakn">
            获取STS</a></div>
</div>
<script>
    //兼容IE11
    if (!FileReader.prototype.readAsBinaryString) {
        FileReader.prototype.readAsBinaryString = function (fileData) {
            var binary = "";
            var pt = this;
            var reader = new FileReader();
            reader.onload = function (e) {
                var bytes = new Uint8Array(reader.result);
                var length = bytes.byteLength;
                for (var i = 0; i < length; i++) {
                    binary += String.fromCharCode(bytes[i]);
                }
                //pt.result  - readonly so assign binary
                pt.content = binary;
                pt.onload()
            }
            reader.readAsArrayBuffer(fileData);
        }
    }
    $(document).ready(function () {
        /**
         * 创建一个上传对象
         * 使用 STSToken 上传方式
         */
        function createUploader() {
            var uploader = new AliyunUpload.Vod({
                timeout: $('#timeout').val() || 60000,
                partSize: $('#partSize').val() || 1048576,
                parallel: $('#parallel').val() || 5,
                retryCount: $('#retryCount').val() || 3,
                retryDuration: $('#retryDuration').val() || 2,
                region: $('#region').val(),
                userId: $('#userId').val(),
                // 添加文件成功
                addFileSuccess: function (uploadInfo) {
                    $('#stsUpload').attr('disabled', false)
                    $('#resumeUpload').attr('disabled', false)
                    $('#status').text('添加文件成功, 等待上传...')
                    console.log("addFileSuccess: " + uploadInfo.file.name)
                },
                // 开始上传
                onUploadstarted: function (uploadInfo) {
                    // 如果是 STSToken 上传方式, 需要调用 uploader.setUploadAuthAndAddress 方法
                    // 如果是 STSToken 上传方式, 需要调用 uploader.setUploadAuthAndAddress 方法
                    // 用户需要自己获取 accessKeyId, accessKeySecret,secretToken
                    // 下面的 URL 只是测试接口, 用于获取 测试的 accessKeyId, accessKeySecret,secretToken
                    console.log(uploadInfo)
                    var stsUrl = '/free/sts'
                    $.ajax({
                        url: stsUrl,
                        type: 'POST',
                        async: false,
                        success: function (e) {
                            console.log(e.data)
                            let accessKeyId = e.data.appid;
                            let accessKeySecret = e.data.secret;
                            // let
                            let secretToken = e.data.token;
                            uploader.setSTSToken(uploadInfo, accessKeyId, accessKeySecret, secretToken)
                        }
                    })
                    // $.post(stsUrl, function (data) {

                    $('#status').text('文件开始上传...')
                    console.log("onUploadStarted:" + uploadInfo.file.name + ", endpoint:" + uploadInfo.endpoint + ", bucket:" + uploadInfo.bucket + ", object:" + uploadInfo.object)
                },
                // 文件上传成功
                onUploadSucceed: function (uploadInfo) {
                    console.log('upload End = ',uploadInfo)
                    let videoId = uploadInfo.videoId
                    console.log('要播放这个视频,你先要将 videoId 传给后端,然后授权播放 = ',videoId)

                    console.log("onUploadSucceed: " + uploadInfo.file.name + ", endpoint:" + uploadInfo.endpoint + ", bucket:" + uploadInfo.bucket + ", object:" + uploadInfo.object)
                    $('#status').text('文件上传成功!')
                },
                // 文件上传失败
                onUploadFailed: function (uploadInfo, code, message) {
                    console.log("onUploadFailed: file:" + uploadInfo.file.name + ",code:" + code + ", message:" + message)
                    $('#status').text('文件上传失败!')
                },
                // 取消文件上传
                onUploadCanceled: function (uploadInfo, code, message) {
                    console.log("Canceled file: " + uploadInfo.file.name + ", code: " + code + ", message:" + message)
                    $('#status').text('文件已暂停上传!')

                },
                // 文件上传进度,单位:字节, 可以在这个函数中拿到上传进度并显示在页面上
                onUploadProgress: function (uploadInfo, totalSize, progress) {
                    console.log("onUploadProgress:file:" + uploadInfo.file.name + ", fileSize:" + totalSize + ", percent:" + Math.ceil(progress * 100) + "%")
                    var progressPercent = Math.ceil(progress * 100)
                    $('#sts-progress').text(progressPercent)
                    $('#status').text('文件上传中...')

                },
                // 上传凭证超时
                onUploadTokenExpired: function (uploadInfo) {
                    // 如果是上传方式二即根据 STSToken 实现时,从新获取STS临时账号用于恢复上传
                    // 上传文件过大时可能在上传过程中 sts token 就会失效, 所以需要在 token 过期的回调中调用 resumeUploadWithSTSToken 方法
                    // 这里是测试接口, 所以我直接获取了 STSToken
                    $('#status').text('文件上传超时!')

                    var stsUrl = 'http://demo-vod.cn-shanghai.aliyuncs.com/voddemo/CreateSecurityToken?BusinessType=vodai&TerminalType=pc&DeviceModel=iPhone9,2&UUID=67999yyuuuy&AppVersion=1.0.0'
                    $.get(stsUrl, function (data) {
                        var info = data.SecurityTokenInfo
                        var accessKeyId = info.AccessKeyId
                        var accessKeySecret = info.AccessKeySecret
                        var secretToken = info.SecurityToken
                        var expiration = info.Expiration
                        uploader.resumeUploadWithSTSToken(accessKeyId, accessKeySecret, secretToken, expiration)
                    }, 'json')
                },
                // 全部文件上传结束
                onUploadEnd: function (uploadInfo) {
                    $('#status').text('文件上传完毕!')

                    console.log("onUploadEnd: uploaded all the files")
                }
            })
            return uploader
        }

        var uploader = null

        $('#fileUpload').on('change', function (e) {
            var file = e.target.files[0]
            if (!file) {
                alert("请先选择需要上传的文件!")
                return
            }
            var Title = file.name
            //这个是上传的文件信息
            var userData = JSON.stringify(
                {"Vod": {"Title": "这个是上传的文件名", "CateId": "1000355534"}}
            )
            if (uploader) {
                uploader.stopUpload()
                $('#sts-progress').text('0')
                $('#status').text("")
            }
            uploader = createUploader()
            // 首先调用 uploader.addFile(event.target.files[i], null, null, null, userData)
            // console.log(userData)
            uploader.addFile(file, null, null, null, userData)
            $('#stsUpload').attr('disabled', false)
            $('#pauseUpload').attr('disabled', true)
            $('#resumeUpload').attr('disabled', true)
        })

        $('#stsUpload').on('click', function () {
            // 然后调用 startUpload 方法, 开始上传
            if (uploader !== null) {
                uploader.startUpload()
                $('#stsUpload').attr('disabled', true)
                $('#pauseUpload').attr('disabled', false)
            }
        })

        $('#pauseUpload').on('click', function () {
            if (uploader !== null) {
                uploader.stopUpload()
                $('#resumeUpload').attr('disabled', false)
                $('#pauseUpload').attr('disabled', true)
            }
        })

        $('#resumeUpload').on('click', function () {
            if (uploader !== null) {
                uploader.startUpload()
                $('#resumeUpload').attr('disabled', true)
                $('#pauseUpload').attr('disabled', false)
            }
        })

    })
</script>
</body>
</html>
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
{
    "file": {},
    "_endpoint": null,
    "_bucket": null,
    "_object": null,
    "state": "Success",
    "isImage": false,
    "videoInfo": {
        "Title": "这个是上传的文件名",
        "CateId": "1000355534"
    },
    "userData": "eyJWb2QiOnsiVGl0bGUiOiLov5nkuKrmmK/kuIrkvKDnmoTmlofku7blkI0iLCJDYXRlSWQiOiIxMDAwMzU1NTM0In19",
    "ri": "A2D9DF1D-ADE5-4BC7-ADD1-87E20F0890FA",
    "fileHash": "c1459b248ab84d92a17f71f0b352d454",
    "retry": false,
    "videoId": "b3e2e84966254c32beeff0c307aebc86",
    "endpoint": "https://oss-cn-shanghai.aliyuncs.com",
    "bucket": "outin-7aa37c7849b711ecb43e00163e00b174",
    "object": "sv/20bd5ad2-17d85b15e77/20bd5ad2-17d85b15e77.mkv",
    "region": "cn-shanghai",
    "loaded": 1,
    "checkpoint": {
        "file": {},
        "name": "sv/20bd5ad2-17d85b15e77/20bd5ad2-17d85b15e77.mkv",
        "fileSize": 635803,
        "partSize": 1048576,
        "uploadId": "06FA48ABA96444468C4ABD5DE670CB5D",
        "doneParts": [
            {
                "number": 1,
                "etag": "\"EEC28156DA1AAE34B05B1372AD4FF105\""
            }
        ]
    }
}

授权播放

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51

    // @Test
    void getPlayUrl(String vid) {
        try {
            String regionId = "cn-shanghai";  // 点播服务接入区域
            System.out.println(prop);
            DefaultProfile profile = DefaultProfile.getProfile(regionId, prop.getAccessKey(), prop.getAccessKeySecret());
            DefaultAcsClient client = new DefaultAcsClient(profile);

            // DefaultAcsClient client = new DefaultAcsClient(prop.get)

            /*InitObject.initVodClient("your accessKeyId","your accessKeySecret");*/
            //创建获取视频地址的request和response
            // IAcsClient client = new DefaultAcsClient(profile);
            GetPlayInfoRequest request = new GetPlayInfoRequest();
            // 视频ID。
            String videoId =

                            vid;
            request.setVideoId(videoId);
            try {
                GetPlayInfoResponse response = client.getAcsResponse(request);
                System.out.println(new Gson().toJson(response));
                for (GetPlayInfoResponse.PlayInfo playInfo : response.getPlayInfoList()) {
                    // 播放地址
                    String playURL = playInfo.getPlayURL();
                    System.out.println("PlayInfo.PlayURL = " + playURL);
                    // return playURL;
                }
            } catch (ClientException e) {
                e.getErrorCode();
                e.getErrorMessage();
                e.getRequestId();
            } catch (com.aliyuncs.exceptions.ServerException e) {
                e.printStackTrace();
            } catch (com.aliyuncs.exceptions.ClientException e) {
                e.printStackTrace();
            }

        } catch (ClientException e) {
            e.printStackTrace();
        }

    }

    @Test
    void printA() {
        this.getPlayUrl("abf6bfc4f9d6443eae32873090c743dd");
        // this.getPlayUrl("5ed03b25bf7d4da296732e390c10c2ed");
        // this.getPlayUrl("6e41e75987324db2a20e5dbb0da580d0");
    }