阿里云OSS使用RAM生成STS分片上传大文件Demo

Posted 赵侠客

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了阿里云OSS使用RAM生成STS分片上传大文件Demo相关的知识,希望对你有一定的参考价值。

阿里云普通Form表单上传视频

这是最简单的一种上传方式,使用后台生成Token 前端通过Form表单直传OSS服务器

后台生成Token代码

    /**
     * 生成阿里云上传Token
     * @return
     * @throws UnsupportedEncodingException
     */
    @GetMapping("/token")
    public ResponseEntity<ApiResult> token() throws UnsupportedEncodingException 
        //上传地址
        String uploadUrl = "http://bucketName.oss-cn-hangzhou.aliyuncs.com/";
        String dir = "";
        OSSClient client = new OSSClient(STSUtil.END_POINT, STSUtil.accessKeyId, STSUtil.accessKeySecret);
        long expireEndTime = System.currentTimeMillis() + STSUtil.expirationTime * 1000;
        Date expiration = new Date(expireEndTime);
        PolicyConditions policyConds = new PolicyConditions();
        policyConds.addConditionItem(PolicyConditions.COND_CONTENT_LENGTH_RANGE, 0, 1048576000);
        policyConds.addConditionItem(MatchMode.StartWith, PolicyConditions.COND_KEY, dir);
        String postPolicy = client.generatePostPolicy(expiration, policyConds);
        byte[] binaryData = postPolicy.getBytes("utf-8");
        String encodedPolicy = BinaryUtil.toBase64String(binaryData);
        String postSignature = client.calculatePostSignature(postPolicy);
        //OSS文件名
        String key = String.format("%s.mp4", UidUtils.generateObjectId());
        JSONObject jasonCallback = new JSONObject();
        //你的回调URL
        jasonCallback.put("callbackUrl", "http://a.com/endpoint/aliyun/uploadSuccess");
		//阿里云回调返回参数
        jasonCallback.put("callbackBody", "id=" + key + "&filename=$object&type=video&etag=$etag&size=$size&mimeType=$mimeType");
        jasonCallback.put("callbackBodyType", "application/x-www-form-urlencoded");
        String base64CallbackBody = BinaryUtil.toBase64String(jasonCallback.toString().getBytes());
        Map<String, String> res = new HashMap<>();
        res.put("callback", base64CallbackBody);
        res.put("key", key);
        res.put("OSSAccessKeyId", STSUtil.accessKeyId);
        res.put("policy", encodedPolicy);
        res.put("success_action_status", "200");
        res.put("signature", postSignature);
        res.put("dir", dir);
        res.put("uploadUrl", uploadUrl);
        res.put("expire", String.valueOf(expireEndTime / 1000));
        return renderOk(res);
    
//返回数据

    "code": 0,
    "data": 
        "OSSAccessKeyId": "LTAI4GHdsbaaaaJhK",
        "uploadUrl": "http://bucketName.oss-cn-hangzhou.aliyuncs.com/",
        "signature": "XdVOT2/HiOals03pQjBaR16Cd9o=",
        "success_action_status": "200",
        "expire": "1616640488",
        "callback": "eyJjYWxsYmFja0JvZmaamdHlwsmV9Jm1pbWVUeXBlPSR7bWltZVR5cGV9In0=",
        "dir": "",
        "key": "6688924142276.mp4",
        "policy": "eyJleHBpcmF0aW9uIjOC42MzBaIiiXV19"
    


前端通过表单上传

<div class="main">
    <input onchange="getToken()"
           accept=".pdf,.doc,.docx,.xls,.xlsx,.ppt,.pptx" id="file" name="file"
           class="ipt" type="file"/>
</div>

<script>
    function getToken() 
        var xhr = new XMLHttpRequest();
        xhr.open('POST',  "/token", true);
        var formData = new FormData();
        xhr.onload = function (e) 
            var res = JSON.parse(this.responseText);
            if (res.code == 0) 
                uploadFile(res, file);
            
        ;
        xhr.send(formData);
    
    function uploadFile(data, file) 
        var xhr = new XMLHttpRequest();
        xhr.open('POST', data.uploadUrl, true);
        var formData, startDate;
        formData = new FormData();
        imgInfoId = data.id;
        formData.append("key",data.key)
        formData.append('policy', data.policy);
        formData.append('OSSAccessKeyId', data.OSSAccessKeyId);
        formData.append('success_action_status',200);
        formData.append('callback',data.callback);
        formData.append('signature',data.signature);
        formData.append('file', file.files[0]);
        var taking;
        xhr.upload.addEventListener("progress", function (evt) 
            if (evt.lengthComputable) 
                var nowDate = new Date().getTime();
                taking = nowDate - startDate;
                var x = (evt.loaded) / 1024;
                var y = taking / 1000;
                var uploadSpeed = (x / y);
                var formatSpeed;
                if (uploadSpeed > 1024) 
                    formatSpeed = (uploadSpeed / 1024).toFixed(2) + "Mb\\/s";
                 else 
                    formatSpeed = uploadSpeed.toFixed(2) + "Kb\\/s";
                
                var percentComplete = Math.round(evt.loaded * 100 / evt.total);
                //计算进度及速度
                console.log(percentComplete, ",", formatSpeed);
            
        , false);

        xhr.onreadystatechange = function (response) 
            if (xhr.readyState == 4 && xhr.status == 200 && xhr.responseText != "") 
                console.info("success")
             else if (xhr.status != 200 && xhr.responseText) 
				console.info("error")
            
        ;
        startDate = new Date().getTime();
        xhr.send(formData);

    

</script>



方法特点

这种上传方式容易简单,缺点是上传速度慢,不支持大文件上传,文件大小数量在G级别的无法使用,而且容易造成浏览器卡顿,所以大文件需要使用分片上传

STS分片上传

配置RAM

1.RAM创建用户,并生成AK SK,上传时使用这组AK/SK

同时给这个用户分配 生成STS和控制OSS权限
AliyunSTSAssumeRoleAccess
AliyunOSSFullAccess

2.创建RAM角色,给ARN分配

3.Bucket配置 OSS跨域增加Etag及x-oss-request-id 暴露Headers 允许跨越上传

后端生成STS Token


    /**
     * 生成STS token
     * @return
     * @throws ClientException
     */
    @GetMapping("/sts")
    public ResponseEntity<ApiResult> sts() throws ClientException 
        Map<String, Object> response = new HashMap<>();
        Map<String, Object> callback = new HashMap<>();
        callback.put("callbackBodyType", "application/x-www-form-urlencoded");
        callback.put("callbackUrl", "http://a.com/endpoint/aliyun/uploadSuccess");
        callback.put("callbackBody", "id=123123123&filename=$object&type=video&etag=$etag&size=$size&mimeType=$mimeType");
        AssumeRoleResponse.Credentials credentials = STSUtil.createSTSForPutObject(STSUtil.bucketName, STSUtil.ROLE_ARN, STSUtil.accessKeyId, STSUtil.accessKeySecret, STSUtil.expirationTime);
        Map<String, Object> res = mapOf("credentials", credentials, "fileKey", UidUtils.generateObjectId() + ".mp4", "bucket", STSUtil.bucketName, "region", STSUtil.REGION_CN_HANGZHOU, "endpoint", STSUtil.END_POINT);
        response.put("callback", callback);
        response.putAll(res);
        return renderOk(response);
    

前端分片上传

<html>
<head>
       
    <script src="https://meizi-tm-5108-pub.oss-cn-hangzhou.aliyuncs.com/lib/aliyun-upload-sdk/lib/aliyun-oss-sdk-6.13.0.min.js"></script>
    <script src="https://meizi-tm-5108-pub.oss-cn-hangzhou.aliyuncs.com/lib/aliyun-upload-sdk/lib/base64.js"></script>

</head>
<body>
  <input type="file" id="file"/>
<span id="process"></span>
<span id="res"></span> 

<script type="text/javascript">
    document.getElementById('file').addEventListener('change', function (e) 
        var file = e.target.files[0];
        //获取STS
        OSS.urllib.request("/sts",
            method: 'GET',
            function (err, response) 
                if (err) 
                    return alert(err);
                
                try 
                    result = JSON.parse(response);
                 catch (e) 
                    return alert('parse sts response info error: ' + e.message);
                
                //64位编码 
                var parses = function (data) 
                    var base = new Base64();
                    return base.encode(JSON.stringify(data));
                
                var client = new OSS(
                    accessKeyId: result.data.credentials.accessKeyId,
                    accessKeySecret: result.data.credentials.accessKeySecret,
                    stsToken: result.data.credentials.securityToken,
                    endpoint: result.data.endpoint,
                    bucket: result.data.bucket,
                    region: result.data.region,
                );
                client.multipartUpload(result.data.fileKey, file, 
                    parallel: 4,
                    partSize: 1024 * 1024,
                    mime: 'video/mp4',
                    headers: 
                        'x-oss-callback': parses(result.data.callback)
                    ,
                    progress: function (p, checkpoint) 
                        document.getElementById('process').innerHTML = p * 100 + "%";
                    
                ).then(function (result) 
                    console.log(result);
                    document.getElementById('res').innerHTML = "上传成功:" + JSON.parse(result).data.filename;
                ).catch(function (err) 
                    console.log(err);
                );
            );
    );

</script>
</body>
</html>

完成分片上传

调通过程中遇到了很多和权限相关的问题,阿里这个RAM确实有点复杂,第一次上手有点门槛,不过这套权限管控确实很好,可以精确控制生成的AK/SK访问云上任意资源的 CRUD权限,安全性是得到保障的。

STSUtil.java

package com.zjrb.media.base.util.aliyun;

import com.alibaba.fastjson.JSON;
import com.aliyun.oss.OSS;
import com.aliyun.oss.OSSClientBuilder;
import com.aliyuncs.DefaultAcsClient;
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 com.aliyuncs.sts.model.v20150401.AssumeRoleRequest;
import com.aliyuncs.sts.model.v20150401.AssumeRoleResponse;
import com.aliyuncs.sts.model.v20150401.AssumeRoleResponse.Credentials;

import java.io.File;

/**
 * @version 1.0
 * @describe: OSS临时访问凭证授权
 * @author:houkai
 * @Date: 2018/3/7 14:54
 */
public class STSUtil 

    /**
     * 目前只有"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";
    /**
     * 必须是https请求
     */
    public static final ProtocolType PROTOCOL_TYPE = ProtocolType.HTTPS;
    /**
     * 指定角色的全局资源描述符(Aliyun Resource Name,简称Arn)
     */
    public static final String ROLE_ARN = "acs:ram::1717083:role/media-upload";
    /**
     * 用户自定义参数。此参数用来区分不同的Token,可用于用户级别的访问审计
     */
    public static final String ROLE_SESSION_NAME = "test";
    /**
     * 阿里云EndPoint
     */
    public static final String END_POINT = "http://oss-cn-hangzhou.aliyuncs.com/";
    /**
     * 上传的Bucket
     */
    public static final String bucketName = "bucketName";
    /**
     * RAM里的用户 AK,不可用全局AK
     */
    public static final String accessKeyId = "aa";
    /**
     * RAM里的用户 SK,不可用全局SK
     */
    public static final String accessKeySecret = "aabb";
    /**
     * 生成的ak sk 过期时间
     */
    public static final  Long expirationTime = 900L;

    public static void main(String[] args) throws Exception 

        Credentials credentials = createSTSForPutObject(bucketName, ROLE_ARN, accessKeyId, accessKeySecret, expirationTime);
        System.out.println(JSON.toJSONString(credentials));
        OSS ossClient = new OSSClientBuilder().build(END_POINT, credentials.getAccessKeyId(), credentials.getAccessKeySecret(), credentials.getSecurityToken());
        ossClient.putObject(bucketName, "1.mp4", new File("C:\\\\Users\\\\Administrator\\\\Videos\\\\朝天门.mp4"));
    

    /**
     * 创建上传临时账号
     *
     * @param bucketName
     * @param roleArn         需要授权的角色名称
     * @param accessKeyId     账号
     * @param accessKeySecret 密码
     * @param expirationTime  过期时间,单位为秒
     * @return
     */
    以上是关于阿里云OSS使用RAM生成STS分片上传大文件Demo的主要内容,如果未能解决你的问题,请参考以下文章

浅谈阿里云OSS分片上传文件

阿里云OSS文件上传(分片上传断点续传)前后端实现

阿里云OSS文件上传(分片上传断点续传)前后端实现

阿里云OSS文件上传(分片上传断点续传)前后端实现

Vue上传阿里云OSS(STS方式)

阿里云OSS服务开通STS安全令牌