大文件上传阿里云oss,分片、断点续传进度条展示

本文介绍了在前端使用阿里云OSS进行大文件上传时,如何通过分片上传和断点续传来解决内存占用过高和上传失败的问题。文章详细讲解了STS临时访问凭证的获取过程,并提供了Java代码示例。同时,展示了JavaScript直传代码,包括文件选择、进度条展示、暂停和恢复上传功能。最后,提到了文件MD5值的计算方法。
摘要由CSDN通过智能技术生成

前端页面展示
在这里插入图片描述

1.背景

大文件如果不采用分片上传会导致卡死、内存占用过高导致程序奔溃等一些列问题。 通常在文件大于100 MB的情况下,建议采用分片上传的方法,通过断点续传和重试,提高上传成功率。如果在文件小于100 MB的情况下使用分片上传,且partSize设置不合理的情况下,可能会出现无法完整显示上传进度的情况。对于小于100 MB的文件,建议使用简单上传的方式。

2.使用STS临时访问凭证访问OSS

因为采用前端js上传,为避免暴露阿里云账号访问密钥(AccessKey ID和AccessKey Secret),强烈建议您使用临时访问凭证的方式执行OSS相关操作
请访问阿里云官网 : 阿里云官网STS操作
临时访问凭证包括临时访问密钥(AccessKey ID和AccessKey Secret)和安全令牌(SecurityToken)。

3.从后台获取临时token获取操作:

   public class OssGetStsToken {

    private static final Logger log = LoggerFactory.getLogger(OssGetStsToken.class);

    private static String accessKeyId = "您的accessKeyId";
    private static String accessKeySecret = "您的accessKeySecret";
    private static String roleArn = "您的roleArn";
    private static String roleSessionName = "您的roleSessionName ";

    /**
     * token失效时间,单位秒(不设置默认1小时,这里设置20分钟)
     */
    private static final Long durationSeconds = 1200L;
    private static final String ENDPOINT = "sts.aliyuncs.com";


    /**
     * 获取STStoken接口
     *
     * @param:
     * @return: StsTokenVO
     */
    public static StsTokenVO getStsToken(String bucketName) {
        StsTokenVO tokenVO = new StsTokenVO();
        try {
            // 添加endpoint(直接使用STS endpoint,前两个参数留空,无需添加region ID)
            DefaultProfile.addEndpoint("", "", "Sts", ENDPOINT);
            // 构造default profile(参数留空,无需添加region ID)
            IClientProfile profile = DefaultProfile.getProfile("", accessKeyId, accessKeySecret);
            // 用profile构造client
            DefaultAcsClient client = new DefaultAcsClient(profile);
            final AssumeRoleRequest request = new AssumeRoleRequest();
            request.setMethod(MethodType.POST);
            request.setRoleArn(roleArn);
            request.setRoleSessionName(roleSessionName);
//             request.setDurationSeconds(durationSeconds);
            // 针对该临时权限可以根据该属性赋予规则,格式为json,没有特殊要求,默认为空
            // request.setPolicy(policy); // Optional
            final AssumeRoleResponse response = client.getAcsResponse(request);
            AssumeRoleResponse.Credentials credentials = response.getCredentials();
            tokenVO.setKeyId(credentials.getAccessKeyId());
            tokenVO.setKeySecret(credentials.getAccessKeySecret());
            tokenVO.setSecurityToken(credentials.getSecurityToken());
            tokenVO.setBucketName(bucketName);
            return tokenVO;
        } catch (ClientException e) {
            log.error("获取阿里云STS临时授权权限失败,错误信息:" + e);
            throw new BaseException("获取阿里云STS临时授权权限失败,错误信息:" + e);
        }
    }
}

StsTokenVO

 @Data
public class StsTokenVO implements Serializable {

    /**
     * 访问密钥标识
     */
    private String keyId;
    /**
     * 访问密钥
     */
    private String keySecret;
    /**
     * 安全令牌
     */
    private String securityToken;

    /**
     * oss-bucket
     */
    private String bucketName;
}

4.js直传代码

<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>OSS上传大文件</title>
</head>
<head>
    <th:block th:include="include :: header('新增ota文件')"/>
    <th:block th:include="include :: jasny-bootstrap-css"/>
</head>

<form>
    <div class="form-group">
        <label class="col-sm-3 control-label">选择ota包:</label>
        <div class="fileinput fileinput-new col-sm-6" data-provides="fileinput">
                    <span id="changeFile" class="btn btn-white btn-file">
                        <span  class="fileinput-new">选择ota包</span>
                        <span  class="fileinput-exists">更改</span>
                        <input type="file" id="firmware" required multiple="multiple"/>
                    </span>
            <span class="fileinput-filename"></span>
            <a href="#" id="labels" class="close fileinput-exists" onclick="empty()" data-dismiss="fileinput" style="float: none">&times</a>
        </div>
        
        <!-- 进度条展示   -->
        <div id="up_wrap" style="margin-left: 420px;width: 800px;"></div>
        
    </div>
</form>
    <div class="row" style="line-height:95px">
        <div class="col-sm-offset-5 col-sm-10">
            <button type="button" class="btn btn-sm btn-warning" id="pause" onclick="pause()"><i class="fa fa-close"> </i> 暂停</button>&nbsp;
            <button type="button" class="btn btn-sm btn-success" id="resume" onclick="resume()"><i class="fa fa-cloud-upload"> </i> 恢复上传</button>&nbsp;
            <button type="button" class="btn btn-sm btn-primary" id="btnsubmit" onclick="submitHandler()"><i class="fa fa-check"> </i> 保 存</button>&nbsp;
            <button type="button" class="btn btn-sm btn-danger"  id="closesubmit" onclick="closeItem()"><i class="fa fa-reply-all"> </i> 关 闭 </button>
        </div>
    </div>
<body>
<th:block th:include="include :: jasny-bootstrap-js"/>
<script th:src="@{/js/plugins/spark-md5.js}"></script>
<script th:src="@{/js/aliyun-oss-sdk-6.16.0.min.js}"></script>
<script th:inline="javascript">
    // 表单提交的路径
    const prefix = ctx + "web/ota"
    // 获取STSToken路径
    const stsRequest = ctx + "web/stsToken";
    // 表单校验
    $("#form-ota-add").validate({
        focusCleanup: true
    });


    //初始化文件上传队列
    var upfiles = [];
    $(function () {
        $("#firmware").change(function (e) {
            var ufiles = $(this).prop('files');
            for (var i = 0; i < ufiles.length; i++) {
                upfiles.push({
                    num: i,
                    name: ufiles[i].name,
                    file: ufiles[i]
                })
            }
            console.log('upfiles:', upfiles);
        })
    })

    // 初始化加载client对象
    var bucket = '';  // bucket名称
    var region = 'oss-cn-shenzhen'; // 地区
    var Buffer = OSS.Buffer;
    var OSS = OSS;
    var md5 = '';
    // 定义中断点数据保存。
    let client;
    let abortCheckpoint;
    var suffix ='';
    var applyTokenDo = function (func) {
        $.ajax({
            url: stsRequest + "/credential",
            type: "get",
            data: '',
            contentType: false,
            processData: false,
            success: function (result) {
                console.log(result);
                client = new OSS({
                    region: region,
                    accessKeyId: result.keyId, //访问密钥标识
                    accessKeySecret: result.keySecret, // 访问密钥
                    stsToken: result.securityToken, // 安全令牌
                    bucket: result.bucketName,
                });
                bucket=result.bucketName;
                return func(client);
            }
        });
    };

    function empty() {
        upfiles = [];
        $("#firmware").val("");
        let a=document.getElementById("firmware");
        a.length=0
    }

    var path ='ota/beta/';
    // 上传文件 并且计算进度,以进度条方式展示
    var uploadFile = function (client) {
        console.log(client);
        if (upfiles.length < 1) {
            return;
        }

        var timestamp = (new Date()).valueOf();
        var file = upfiles[0].file;
        var key = upfiles[0].name;
        $('#btnsubmit').attr('disabled', true);
        $('#closesubmit').attr('disabled', true);
        $("#pause").show();
        $("#resume").show();
        suffix = path + timestamp + key.substring(key.length - 4);
        // 分片上传
        return client.multipartUpload(suffix, file, {
            progress: function (p, cpt, res) {
                abortCheckpoint = cpt;
                let ps = parseInt((p.toFixed(2)) * 100);
                console.log("上传进度:", ps + '%');
                console.log("cpt:", cpt);
                let html = '';
                html = '<dl><dt></dt><dd><div class="progress"><div  class="progress-bar"  role="progressbar" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100" style="width:' + ps + '%"><span>' + ps + '%</span></div></div></dd></dl>';
                $("#up_wrap").html(html);
                if (p == 1) {
                    const url = `https://${bucket}.${region}.aliyuncs.com/${suffix}`; // 最终上传完成后的下载url
                    console.log("url:", url);
                    // oss key(删除文件需要用到)
                    $("#ossKey").val(suffix);
                    // 完整的下载地址
                    $("#downloadUrl").val(url);
                    // 文件大小
                    $("#fileSize").val(file.size);
                    // 表单提交
                    if ($.validate.form()) {
                        $.operate.save(prefix + "/add", $('#form-ota-add').serialize());
                    }
                }
                if (cpt != undefined) {
                    var content = JSON.stringify(cpt);
                    client.put(suffix, new Buffer(content));
                }
                return function (done) {
                    var bar = document.getElementById('progress-bar_' + upfile.num);
                    bar.style.width = Math.floor(p * 100) + '%';
                    bar.innerHTML = Math.floor(p * 100) + '%';
                    done();
                }
            }
        }).then(function (res) {
            console.log('上传成功: ', res);
            upfiles.shift();
            client.delete(key);
            applyTokenDo(uploadFile);
        });
    };

    // 表单提交
    function submitHandler() {
        var form = $("#form-ota-add").get(0);
        var formdata = new FormData(form);
        console.log(formdata);
        if ($.validate.form()) {
            $("#closesubmit").attr("disabled",true);
            $("#btnsubmit").attr("disabled",true);
            $("#changeFile").css("pointer-events", "none");
            $("#labels").css("visibility","hidden");
            $.ajax({
                url: prefix + "/addCheck",
                type: "post",
                data: formdata,
                contentType: false,
                processData: false,
                success: function (result) {
                    if (result){
                        applyTokenDo(uploadFile);
                    }else {
                        $.modal.alertError("已有存在的版本号和型号文件!");
                        $("#closesubmit").attr("disabled",false);
                        $("#btnsubmit").attr("disabled",false);
                        empty();
                    }
                }
            });
        }
    }

    // 监听续传按钮,单击”恢复上传“后继续上传。
    // 暂停
    function pause() {
        console.log(client);
        client.cancel();
    }

    // 恢复上传
    function resume() {
        resumeUpload();
    }
    //  隐藏按钮
    $(function(){
        $("#pause").hide();
        $("#resume").hide();
    });

    // 异步断点续传方法 (恢复上传)
    const resumeUpload = async () => {
        console.log(client);
        if (upfiles.length < 1) {
            return;
        }
        var file = upfiles[0].file;
        var key = upfiles[0].name;
        return client.multipartUpload(suffix, file, {
            checkpoint: abortCheckpoint,
            progress: function (p, cpt, res) {
                abortCheckpoint = cpt;
                let ps = parseInt((p.toFixed(2)) * 100);
                console.log("上传进度:", ps + '%');
                console.log("cpt:", cpt);
                let html = '';
                html = '<dl><dt></dt><dd><div class="progress"><div  class="progress-bar"  role="progressbar" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100" style="width:' + ps + '%"><span>' + ps + '%</span></div></div></dd></dl>';
                $("#up_wrap").html(html);
                if (p == 1) {
                    const url = `https://${bucket}.${region}.aliyuncs.com/${suffix}`; // 最终上传完成后的下载url
                    console.log("url:", url);
                    $("#ossKey").val(suffix);
                    $("#downloadUrl").val(url);
                    $("#fileSize").val(file.size);
                    // 表单提交
                    if ($.validate.form()) {
                        $.operate.save(prefix + "/add", $('#form-ota-add').serialize());
                    }
                }
                if (cpt != undefined) {
                    var content = JSON.stringify(cpt);
                    client.put(suffix, new Buffer(content));
                }
                return function (done) {
                    var bar = document.getElementById('progress-bar_' + upfile.num);
                    bar.style.width = Math.floor(p * 100) + '%';
                    bar.innerHTML = Math.floor(p * 100) + '%';
                    done();
                }
            }
        }).then(function (res) {
            console.log('上传成功: ', res);
            upfiles.shift();
            client.delete(key);
            applyTokenDo(uploadFile);
        });
    };


    /**
     * 根据选择的文件生成 MD5值
     */
    document.getElementById('firmware').addEventListener('change', function () {
        var sta = new Date().getTime();
        var blobSlice = File.prototype.slice || File.prototype.mozSlice || File.prototype.webkitSlice,
            file = this.files[0],
            chunkSize = 52428800,                             // 分片大小 (50MB)
            chunks = Math.ceil(file.size / chunkSize),
            currentChunk = 0,
            spark = new SparkMD5.ArrayBuffer(),
            fileReader = new FileReader();

        fileReader.onload = function (e) {
            console.log('read chunk nr', currentChunk + 1, 'of', chunks);
            spark.append(e.target.result);                   // Append array buffer
            currentChunk++;

            if (currentChunk < chunks) {
                loadNext();
            } else {
                var end = new Date().getTime() - sta;
                console.info('耗时:', end)
                $("#otaMd5").val(spark.end());
                console.info('MD5:', $("#otaMd5").val())
            }
        };
        fileReader.onerror = function () {
            console.warn('oops, something went wrong.');
        };
        function loadNext() {
            var start = currentChunk * chunkSize,
                end = ((start + chunkSize) >= file.size) ? file.size : start + chunkSize;
            fileReader.readAsArrayBuffer(blobSlice.call(file, start, end));
        }
        loadNext();
    });
</script>
</body>
</html>
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值