阿里云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
     */
    public static Credentials createSTSForPutObject(String bucketName, String roleArn, String accessKeyId, String accessKeySecret, Long expirationTime) throws ClientException {
    
    
        String policy = STSUtil.getPutObjectPolicy(bucketName);
        return createSTS(policy, roleArn, accessKeyId, accessKeySecret, expirationTime);
    }

    /**
     * 创建只读临时授权
     *
     * @param bucketName
     * @param roleArn         需要授权的角色名称
     * @param accessKeyId     账号
     * @param accessKeySecret 密码
     * @param expirationTime  过期时间,单位为秒
     * @return
     */
    public static Credentials createSTSForReadOnly(String bucketName, String roleArn, String accessKeyId, String accessKeySecret, Long expirationTime) throws ClientException {
    
    
        String policy = STSUtil.getOSSReadOnlyAccessPolicy(bucketName);
        return createSTS(policy, roleArn, accessKeyId, accessKeySecret, expirationTime);
    }

    /**
     * 创建STS
     *
     * @param policy          授权策略
     * @param roleArn         需要授权的角色名称
     * @param accessKeyId     账号
     * @param accessKeySecret 密码
     * @param expirationTime  过期时间,单位为秒
     * @return
     */
    private static Credentials createSTS(String policy, String roleArn, String accessKeyId, String accessKeySecret, Long expirationTime) throws ClientException {
    
    
        IClientProfile profile = DefaultProfile.getProfile(REGION_CN_HANGZHOU, accessKeyId, accessKeySecret);
        DefaultAcsClient client = new DefaultAcsClient(profile);
        final AssumeRoleRequest request = new AssumeRoleRequest();
        request.setDurationSeconds(expirationTime);
        request.setVersion(STS_API_VERSION);
        request.setMethod(MethodType.POST);
        request.setProtocol(PROTOCOL_TYPE);
        request.setRoleArn(roleArn);
        request.setRoleSessionName(ROLE_SESSION_NAME);
        request.setPolicy(policy);
        //实体用户获取角色身份的安全令牌的方法
        AssumeRoleResponse response = client.getAcsResponse(request);
        Credentials credentials = response.getCredentials();
        return credentials;
    }

    /**
     * 自定义授权策略,对当前bucket下的文件夹读写
     *
     * @param bucketName
     * @return
     */
    private static String getPutObjectPolicy(String bucketName) {
    
    
        return String.format(
                "{\n" +
                        "    \"Version\": \"1\", \n" +
                        "    \"Statement\": [\n" +
                        "        {\n" +
                        "            \"Action\": [\n" +
                        "                \"oss:PutObject\" \n" +
                        "            ], \n" +
                        "            \"Resource\": [\n" +
                        "                \"acs:oss:*:*:%s/*\"\n" +
                        "            ], \n" +
                        "            \"Effect\": \"Allow\"\n" +
                        "        }\n" +
                        "    ]\n" +
                        "}", bucketName);
    }

    /**
     * 只读访问该bucket对象存储服务(OSS)的权限,授权策略
     *
     * @param bucketName
     * @return
     */
    private static String getOSSReadOnlyAccessPolicy(String bucketName) {
    
    
        return String.format("{\n" +
                "  \"Statement\": [\n" +
                "    {\n" +
                "      \"Action\": [\n" +
                "        \"oss:Get*\",\n" +
                "        \"oss:List*\"\n" +
                "      ],\n" +
                "      \"Effect\": \"Allow\",\n" +
                "      \"Resource\": [\n" +
                "        \"acs:oss:*:*:%s/*\"\n" +
                "      ]\n" +
                "    }\n" +
                "  ],\n" +
                "  \"Version\": \"1\"\n" +
                "}", bucketName);
    }

}

猜你喜欢

转载自blog.csdn.net/whzhaochao/article/details/115200160