前端页面展示

axios上传大文件设置 oss上传大文件_上传

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">×</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> 
            <button type="button" class="btn btn-sm btn-success" id="resume" onclick="resume()"><i class="fa fa-cloud-upload"> </i> 恢复上传</button> 
            <button type="button" class="btn btn-sm btn-primary" id="btnsubmit" onclick="submitHandler()"><i class="fa fa-check"> </i> 保 存</button> 
            <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>