web实现断点续传

 普通单文件上传:

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>断点续传</title>

</head>
<body>
<div id="app">
    <input type="file" id="file-input"/><button id="upload-button">上传</button>
</div>

<script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.js"></script>
<script>
document.getElementById('file-input').addEventListener('change', fileSelect);
document.getElementById("upload-button").addEventListener('click', uploadFile);

var uploadConfig = {
    chunkMaxSize: 1024*1024*5 //分片大小,这里是5M
}

// 选择文件时
function fileSelect(){
    var file = document.getElementById("this-file").files[0]; //需要上传的文件

    // 整个文件信息
    selectedFile = {
        md5: fileMd5, //spark-md5计算出的hash值
        name: file.name //所上传文件名称
    }

    // 上传参数
    chunkParam = {
        chunk: 0, //开始上传的分片索引
        isLastChunk: false, //是否是最后一个分片
        chunkLength: Math.ceil(file.size/uploadConfig.chunkMaxSize) //分片个数
    }
}

// 上传每一个文件前
function uploadFile(file){
    $.ajax({
		type: "POST",
		url: '/checkFile',
		data: selectedFile,
		success: function(data){
            if(data.state == 0){ //判断之前是否上传成功此文件,0未上传
                chunksFile(); //上传文件
            }else if(data.state == 1){ // 上传了部分,将查询到的已上传结果赋值给表单中的分片
                chunkParam.chunk = parseInt(data.maxChuck) + 1;
                chunksFile();
            }else if(data.state == 2){
                alert("该文件已经上传过");
            }
		},
		error: function(data){
			console.log(data);
		}
	});
}

// 上传每一个分片时
function chunksFile(){
    //判断是不是最后一片,通知后台
    (chunkParam.chunk+1) == chunkLength ? chunkParam.isLastChunk = true : chunkParam.isLastChunk = false;

    //用formData通过ajax上传文件内容
    var fm = new FormData();
    fm.append('fileMd5', selectedFile.md5);//spark-md5计算出来的md5值
    fm.append('chunk', chunkParam.chunk);
    fm.append('isLastChunk ', chunkParam.isLastChunk );
    fm.append('fileName ', selectedFile.fileName);
    fm.append('theChunk', file.slice(chunkParam.chunk*uploadConfig.chunkMaxSize, (chunkParam.chunk+1)*uploadConfig.chunkMaxSize));

    $.ajax({
		type: "POST",
		url: '/uploadChuncks',
		data: fm,
		success: function(data){
            if(data.ret){
                // 上传完最后一片后传输结束
                if(chunkParam.isLastChunk){
                    console.log("上传完成");
                }else{
                    // 上传成功,分片加一,上传下一片文件
                    chunkParam.chunk = parseInt(data.chunk) + 1;
                    //递归调用上传下一片
                    chunksFile();
                }
              }else{
                console.log(data.msg);
              }
		},
		error: function(data){
			console.log(data.msg);
		}
	});
}
</script>
</body>
</html>

单文件上传,有进度条,可以对文件加密:

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>断点续传</title>

</head>
<body>
<div id="app">
    <input type="file" id="file-input"/><button id="upload-button">上传</button>
    <p id="progress"></p>
</div>

<script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.js"></script>
<script>
document.getElementById('file-input').addEventListener('change', fileSelect);
document.getElementById("upload-button").addEventListener('click', uploadFile);

var uploadConfig = {
    chunkMaxSize: 1024*1024*5 //分片大小,这里是5M
}

// 选择文件时
function fileSelect(){
    var file = document.getElementById("this-file").files[0]; //需要上传的文件

    var reader = new FileReader();
    reader.readAsArrayBuffer(file);
    reader.addEventListener("load", function(e) {
        // 将文件存储到内存
        var fileBlobFormatter = e.target.result;

        // 整个文件信息
        selectedFile = {
            md5: fileMd5, //spark-md5计算出的hash值
            name: file.name, //所上传文件名称,
            fileBlobFormatter
        }

        // 上传参数
        chunkParam = {
            chunk: 0, //开始上传的分片索引
            isLastChunk: false, //是否是最后一个分片
            chunkLength: Math.ceil(file.size/uploadConfig.chunkMaxSize) //分片个数
        }
    });
}

// 上传每一个文件前
function uploadFile(file){
    $.ajax({
		type: "POST",
		url: '/checkFile',
		data: selectedFile,
		success: function(data){
            if(data.state == 0){ //判断之前是否上传成功此文件,0未上传
                chunksFile(); //上传文件
            }else if(data.state == 1){ // 上传了部分,将查询到的已上传结果赋值给表单中的分片
                chunkParam.chunk = parseInt(data.maxChuck) + 1;
                chunksFile();
            }else if(data.state == 2){
                alert("该文件已经上传过");
            }
		},
		error: function(data){
			console.log(data);
		}
	});
}

// 上传每一个分片时
function chunksFile(){
    //判断是不是最后一片,通知后台
    (chunkParam.chunk+1) == chunkLength ? chunkParam.isLastChunk = true : chunkParam.isLastChunk = false;

    //用formData通过ajax上传文件内容
    var fm = new FormData();
    fm.append('fileMd5', selectedFile.md5);//spark-md5计算出来的md5值
    fm.append('chunk', chunkParam.chunk);
    fm.append('isLastChunk ', chunkParam.isLastChunk );
    fm.append('fileName ', selectedFile.fileName);
    fm.append('theChunk', selectedFile.fileBlobFormatter.slice(chunkParam.chunk*uploadConfig.chunkMaxSize, (chunkParam.chunk+1)*uploadConfig.chunkMaxSize));

    $.ajax({
		type: "POST",
		url: '/uploadChuncks',
		data: fm,
		success: function(data){
            if(data.ret){
                // 上传完最后一片后传输结束
                if(chunkParam.isLastChunk){
                    console.log("上传完成");
                }else{
                    //计算上传进度
                    $("#progress").text((chunkParam.chunk+1)/chunkParam.chunkLength+"%");
                    //上传成功,分片加一,上传下一片文件
                    chunkParam.chunk = parseInt(data.chunk) + 1;
                    //递归调用上传下一片
                    chunksFile();
                }
              }else{
                console.log(data.msg);
              }
		},
		error: function(data){
			console.log(data.msg);
		}
	});
}
</script>
</body>
</html>

多文件断点续传:

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<link href="https://cdn.bootcss.com/twitter-bootstrap/4.3.1/css/bootstrap.css" rel="stylesheet">
<style type="text/css">
body {
    font-family: Arial;
}
form {
    margin: 50px auto;
    width: 600px;
}
input[type="button"] {
    cursor: pointer;
}
table {
    display: none;
    margin-top: 15px;
    border: 1px solid #ddd;
    border-collapse: collapse;
}
table th {
    color: #666;
}
table td,table th {
    padding: 5px;
    border: 1px solid #ddd;
    text-align: center;
    font-size: 14px;
}
#upload-list th, #upload-list td{
    text-align: left;
}
</style>
<title>断点续传</title>

</head>
<body>
<form method="post" id="myForm" action="/fileTest.php" enctype="multipart/form-data"> 
    <input type="file" id="myFile" multiple="" class="btn btn-primary btn-xs"/> 
    <!-- 上传的文件列表 --> 
    <table id="upload-list" class="table table-bordered"> 
        <thead> 
        <tr> 
        <th width="35%">文件名</th> 
        <th width="15%">文件类型</th> 
        <th width="15%">文件大小</th> 
        <th width="20%">上传进度</th> 
        <th width="15%"> <input type="button" class="btn btn-primary btn-xs" id="upload-all-btn" value="全部上传" /> </th> 
        </tr> 
        </thead> 
        <tbody> 
        </tbody> 
    </table> 
</form>

<!-- 上传文件列表中每个文件的信息模版 --> 
<script type="text/template" id="file-upload-tpl">
    <tr>
    <td>{{fileName}}</td>
    <td>{{fileType}}</td>
    <td>{{fileSize}}</td>
    <td class="upload-progress">{{progress}}</td>
    <td>
    <input type="button" class="upload-item-btn btn btn-primary btn-xs" data-name="{{fileName}}" data-size="{{totalSize}}" data-state="default" value="{{uploadVal}}">
    </td>
    </tr>
</script>

<script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.js"></script>
<script src="https://cdn.bootcss.com/twitter-bootstrap/4.3.1/js/bootstrap.js"></script>
<script>
// 全部上传操作
$(document).on('click', '#upload-all-btn',function() {
        // 未选择文件
        if (!$('#myFile').val()) {
            $('#myFile').focus();
        }
        // 模拟点击其他可上传的文件
        else {
            $('#upload-list .upload-item-btn').each(function() {
                $(this).click();
            });
        }
    }
);

// 选择文件,显示文件信息
$('#myFile').change(function(e) {
    var file;
    var uploadItem = [];
    var uploadItemTpl = $('#file-upload-tpl').html();
    var size;
    var percent;
    var progress = '未上传';
    var uploadVal = '开始上传';

    for (var i = 0, j = this.files.length; i < j; ++i) {
        file = this.files[i];
        percent = undefined;
        progress = '未上传';
        uploadVal = '开始上传';
        // 计算文件大小
        size = file.size > 1024 ? file.size / 1024 > 1024 ? file.size / (1024 * 1024) > 1024 ? (file.size / (1024 * 1024 * 1024)).toFixed(2) + 'GB': (file.size / (1024 * 1024)).toFixed(2) + 'MB': (file.size / 1024).toFixed(2) + 'KB': (file.size).toFixed(2) + 'B';
        // 初始通过本地记录,判断该文件是否曾经上传过
        percent = window.localStorage.getItem(file.name + '_p');
        if (percent && percent !== '100.0') {
            progress = '已上传 ' + percent + '%';
            uploadVal = '继续上传';
        }
        // 更新文件信息列表
        uploadItem.push(uploadItemTpl.replace(/{{fileName}}/g, file.name).replace('{{fileType}}', file.type || file.name.match(/\.\w+$/) + '文件').replace('{{fileSize}}', size).replace('{{progress}}', progress).replace('{{totalSize}}', file.size).replace('{{uploadVal}}', uploadVal));
    }
    $('#upload-list').children('tbody').html(uploadItem.join('')).end().show();
});

/**
* 上传文件时,提取相应匹配的文件项
* @param {String} fileName 需要匹配的文件名
* @return {FileList} 匹配的文件项目
*/
function findTheFile(fileName) {
    var files = $('#myFile')[0].files;
    var theFile;
    for (var i = 0, j = files.length; i < j; ++i) {
        if (files[i].name === fileName) {
            theFile = files[i];
            break;
        }
    }
    return theFile ? theFile: [];
}
    
// 上传文件
$(document).on('click', '.upload-item-btn', function() {
    var $this = $(this);
    var state = $this.attr('data-state');
    var msg = {
            done: '上传成功',
            failed: '上传失败',
            in:'上传中...',
            paused: '暂停中...'
    };
    var fileName = $this.attr('data-name');
    var $progress = $this.closest('tr').find('.upload-progress');
    var eachSize = 1024; //分片大小
    var totalSize = $this.attr('data-size');
    var chunks = Math.ceil(totalSize / eachSize); //分片数量
    var percent;
    var chunk; //分片序号
    // 暂停上传操作
    isPaused = 0;
    // 进行暂停上传操作
    // 未实现,这里通过动态的设置isPaused值并不能阻止下方ajax请求的调用
    if (state === 'uploading') {
        $this.val('继续上传').attr('data-state', 'paused');
        $progress.text(msg['paused'] + percent + '%');
        isPaused = 1;
        console.log('暂停:', isPaused);
    }
    // 进行开始/继续上传操作
    else if (state === 'paused' || state === 'default') {
        $this.val('暂停上传').attr('data-state', 'uploading');
        isPaused = 0;
    }
    // 第一次点击上传
    startUpload('first');
    // 上传操作 times: 第几次
    function startUpload(times) {
        // 上传之前查询是否以及上传过分片
        chunk = window.localStorage.getItem(fileName + '_chunk') || 0;
        chunk = parseInt(chunk, 10);
        // 判断是否为末分片
        var isLastChunk = (chunk == (chunks - 1) ? 1 : 0);
        // 如果第一次上传就为末分片,即文件已经上传完成,则重新覆盖上传
        if (times === 'first' && isLastChunk === 1) {
            window.localStorage.setItem(fileName + '_chunk', 0);
            chunk = 0;
            isLastChunk = 0;
        }
        // 设置分片的开始结尾
        var blobFrom = chunk * eachSize; // 分段开始
        var blobTo = (chunk + 1) * eachSize > totalSize ? totalSize:(chunk + 1) * eachSize; // 分段结尾
        var percent = (100 * blobTo / totalSize).toFixed(1); // 已上传的百分比
        var timeout = 5000; // 超时时间
        fd = new FormData($('#myForm')[0]);
        fd.append('theFile', findTheFile(fileName).slice(blobFrom, blobTo)); // 分好段的文件
        fd.append('fileName', fileName); // 文件名
        fd.append('totalSize', totalSize); // 文件总大小
        fd.append('isLastChunk', isLastChunk); // 是否为末段
        fd.append('isFirstUpload', times === 'first' ? 1 : 0); // 是否是第一段(第一次上传)
        // 上传
        $.ajax({
            type: 'post',
            url: '/fileTest.php',
            data: fd,
            processData: false,
            contentType: false,
            timeout: timeout,
            success: function(rs) {
                rs = JSON.parse(rs);
                // 上传成功
                if (rs.status === 200) {
                    // 记录已经上传的百分比
                    window.localStorage.setItem(fileName + '_p', percent);
                    // 已经上传完毕
                    if (chunk === (chunks - 1)) {
                        $progress.text(msg['done']);
                        $this.val('已经上传').prop('disabled', true).css('cursor', 'not-allowed');
                        if (!$('#upload-list').find('.upload-item-btn:not(:disabled)').length) {
                            $('#upload-all-btn').val('已经上传').prop('disabled', true).css('cursor', 'not-allowed');
                        }
                    } else {
                        // 记录已经上传的分片
                        window.localStorage.setItem(fileName + '_chunk', ++chunk);
                        $progress.text(msg['in'] + percent + '%');
                        // 这样设置可以暂停,但点击后动态的设置就暂停不了..
                        // if (chunk == 10) {
                        // isPaused = 1;
                        // }
                        console.log(isPaused);
                        if (!isPaused) {
                            startUpload();
                        }
                    }
                }
                // 上传失败,上传失败分很多种情况,具体按实际来设置
                else if (rs.status === 500) {
                    $progress.text(msg['failed']);
                }
            },
            error: function() {
                $progress.text(msg['failed']);
            }
        });
    }
});
</script>
</body>
</html>

多文件断点续传+加密:

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<link href="https://cdn.bootcss.com/twitter-bootstrap/4.3.1/css/bootstrap.css" rel="stylesheet">
<style type="text/css">
body {
    font-family: Arial;
}
form {
    margin: 50px auto;
    width: 600px;
}
input[type="button"] {
    cursor: pointer;
}
table {
    display: none;
    margin-top: 15px;
    border: 1px solid #ddd;
    border-collapse: collapse;
}
table th {
    color: #666;
}
table td,table th {
    padding: 5px;
    border: 1px solid #ddd;
    text-align: center;
    font-size: 14px;
}
#web-disk-file-list th, #web-disk-file-list td{
    text-align: left;
}
</style>
<title>断点续传</title>

</head>
<body>
<form method="post" id="web-disk-file-form" action="/fileTest.php" enctype="multipart/form-data"> 
    <input type="file" id="web-disk-file-input" multiple="" class="btn btn-primary btn-xs"/> 
    <!-- 上传的文件列表 --> 
    <table id="web-disk-file-list" class="table table-bordered"> 
        <thead> 
        <tr> 
        <th width="35%">文件名</th> 
        <th width="15%">文件类型</th> 
        <th width="15%">文件大小</th> 
        <th width="20%">上传进度</th> 
        <th width="15%"> <input type="button" class="btn btn-primary btn-xs" id="web-disk-upload-all-btn" value="全部上传" /> </th> 
        </tr> 
        </thead> 
        <tbody> 
        </tbody> 
    </table> 
</form>

<!-- 上传文件列表中每个文件的信息模版 --> 
<script type="text/template" id="web-disk-file-upload-tpl">
    <tr>
    <td>{{fileName}}</td>
    <td>{{fileType}}</td>
    <td>{{fileSize}}</td>
    <td class="upload-progress">{{progress}}</td>
    <td>
    <input type="button" class="web-disk-upload-item-btn btn btn-primary btn-xs" data-name="{{fileName}}" data-size="{{totalSize}}" data-state="default" value="{{uploadVal}}">
    </td>
    </tr>
</script>

<script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.js"></script>
<script src="https://cdn.bootcss.com/twitter-bootstrap/4.3.1/js/bootstrap.js"></script>
<script>
// 全部上传操作
$(document).on('click', '#web-disk-upload-all-btn',function() {
        // 未选择文件
        if (!$('#web-disk-file-input').val()) {
            $('#web-disk-file-input').focus();
        }
        // 模拟点击其他可上传的文件
        else {
            $('#web-disk-file-list .web-disk-upload-item-btn').each(function() {
                $(this).click();
            });
        }
    }
);

// 选择文件,显示文件信息
$('#web-disk-file-input').change(function(e) {
    var file;
    var uploadItem = [];
    var uploadItemTpl = $('#web-disk-file-upload-tpl').html();
    var size;
    var percent;
    var progress = '未上传';
    var uploadVal = '开始上传';

    for (var i = 0, j = this.files.length; i < j; ++i) {
        file = this.files[i];
        percent = undefined;
        progress = '未上传';
        uploadVal = '开始上传';
        // 计算文件大小
        size = file.size > 1024 ? file.size / 1024 > 1024 ? file.size / (1024 * 1024) > 1024 ? (file.size / (1024 * 1024 * 1024)).toFixed(2) + 'GB': (file.size / (1024 * 1024)).toFixed(2) + 'MB': (file.size / 1024).toFixed(2) + 'KB': (file.size).toFixed(2) + 'B';
        // 初始通过本地记录,判断该文件是否曾经上传过
        percent = window.localStorage.getItem(file.name + '_p');
        if (percent && percent !== '100.0') {
            progress = '已上传 ' + percent + '%';
            uploadVal = '继续上传';
        }
        // 更新文件信息列表
        uploadItem.push(uploadItemTpl.replace(/{{fileName}}/g, file.name).replace('{{fileType}}', file.type || file.name.match(/\.\w+$/) + '文件').replace('{{fileSize}}', size).replace('{{progress}}', progress).replace('{{totalSize}}', file.size).replace('{{uploadVal}}', uploadVal));
    }
    $('#web-disk-file-list').children('tbody').html(uploadItem.join('')).end().show();
});

/**
* 上传文件时,提取相应匹配的文件项
* @param {String} fileName 需要匹配的文件名
* @return {FileList} 匹配的文件项目
*/
function findTheFile(fileName) {
    var files = $('#web-disk-file-input')[0].files;
    var theFile;
    for (var i = 0, j = files.length; i < j; ++i) {
        if (files[i].name === fileName) {
            theFile = files[i];
            break;
        }
    }
    return theFile ? theFile: [];
}
    
// 上传文件
$(document).on('click', '.web-disk-upload-item-btn', function() {
    var $this = $(this);
    var state = $this.attr('data-state');
    var msg = {
            done: '上传成功',
            failed: '上传失败',
            in:'上传中...',
            paused: '暂停中...'
    };
    var fileName = $this.attr('data-name');
    var $progress = $this.closest('tr').find('.upload-progress');
    var eachSize = 1024; //分片大小
    var totalSize = $this.attr('data-size');
    var chunks = Math.ceil(totalSize / eachSize); //分片数量
    var percent;
    var chunk; //分片序号
    
    // 暂停上传操作
    isPaused = 0;
    // 进行暂停上传操作
    // 未实现,这里通过动态的设置isPaused值并不能阻止下方ajax请求的调用
    if (state === 'uploading') {
        $this.val('继续上传').attr('data-state', 'paused');
        $progress.text(msg['paused'] + percent + '%');
        isPaused = 1;
        console.log('暂停:', isPaused);
    }
    // 进行开始/继续上传操作
    else if (state === 'paused' || state === 'default') {
        $this.val('暂停上传').attr('data-state', 'uploading');
        isPaused = 0;
    }
    // 第一次点击上传
    startUpload('first');

    // 上传操作 times: 第几次
    function startUpload(times) {
        // 上传之前查询是否以及上传过分片
        chunk = window.localStorage.getItem(fileName + '_chunk') || 0; //上传到第几片?
        chunk = parseInt(chunk, 10);
        // 判断是否为末分片
        var isLastChunk = (chunk == (chunks - 1) ? 1 : 0);
        // 如果第一次上传就为末分片,即文件已经上传完成,则重新覆盖上传
        if (times === 'first' && isLastChunk === 1) {
            window.localStorage.setItem(fileName + '_chunk', 0);
            chunk = 0;
            isLastChunk = 0;
        }
        // 设置分片的开始结尾
        var blobFrom = chunk * eachSize; // 分段开始
        var blobTo = (chunk + 1) * eachSize > totalSize ? totalSize:(chunk + 1) * eachSize; // 分段结尾
        var percent = (100 * blobTo / totalSize).toFixed(1); // 已上传的百分比
        var timeout = 5000; // 超时时间

        var reader = new FileReader();
        var readerSliceBlob = findTheFile(fileName).slice(blobFrom, blobTo); //分好段的文件
        reader.readAsArrayBuffer(readerSliceBlob);
        reader.addEventListener("load", function(e) {
            // 将文件存储到内存
            var fileBlobFormatter = e.target.result; // 可在此处对文件加密

            // 上传操作 begin===============================================
            fd = new FormData($('#web-disk-file-form')[0]);
            fd.append('theFile', fileBlobFormatter); // 加密分好段的文件
            fd.append('fileName', fileName); // 文件名
            fd.append('totalSize', totalSize); // 文件总大小
            fd.append('isLastChunk', isLastChunk); // 是否为末段
            fd.append('isFirstUpload', times === 'first' ? 1 : 0); // 是否是第一段(第一次上传)
            // 上传
            $.ajax({
                type: 'post',
                url: '/fileTest.php',
                data: fd,
                processData: false,
                contentType: false,
                timeout: timeout,
                success: function(rs) {
                    rs = JSON.parse(rs);
                    // 上传成功
                    if (rs.status === 200) {
                        // 记录已经上传的百分比
                        window.localStorage.setItem(fileName + '_p', percent);
                        // 已经上传完毕
                        if (chunk === (chunks - 1)) {
                            $progress.text(msg['done']);
                            $this.val('已经上传').prop('disabled', true).css('cursor', 'not-allowed');
                            if (!$('#web-disk-file-list').find('.web-disk-upload-item-btn:not(:disabled)').length) {
                                $('#web-disk-upload-all-btn').val('已经上传').prop('disabled', true).css('cursor', 'not-allowed');
                            }
                        } else {
                            // 记录已经上传的分片
                            window.localStorage.setItem(fileName + '_chunk', ++chunk);
                            $progress.text(msg['in'] + percent + '%');
                            // 这样设置可以暂停,但点击后动态的设置就暂停不了..
                            // if (chunk == 10) {
                            // isPaused = 1;
                            // }
                            console.log(isPaused);
                            if (!isPaused) {
                                startUpload();
                            }
                        }
                    }
                    // 上传失败,上传失败分很多种情况,具体按实际来设置
                    else if (rs.status === 500) {
                        $progress.text(msg['failed']);
                    }
                },
                error: function() {
                    $progress.text(msg['failed']);
                }
            });
            // 上传操作 end===============================================
        });
    }
});
</script>
</body>
</html>

 

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值