webuploader实现大文件断点续传

前端代码(基于Yii框架,逻辑可供参考)

<script>
    var fileMd5;  //文件MD5
    var fileObj;  //文件对象
    var state = 'pending';  //状态
    WebUploader.Uploader.register({
        "before-send": "beforeSend",
        "after-send-file": "afterSendFile"
    }, {
        beforeSend: function (block) {
            //每个分片开始上传前检查分片是否存在
            var deferred = WebUploader.Deferred();
            $.post('<?=Url::to(['video-upload/check-chunk'])?>', {
                fileMd5: fileMd5,
                chunk: block.chunk
            }, function (res) {
                //不存在继续下一步
                if (res.code == 1) {
                    deferred.resolve();
                } else {
                    //已存在直接跳过
                    deferred.reject();
                }
            }, 'json');
            //把fileMd5作为参数传入upload接口,配置项里无法直接配置所以放在这里
            this.owner.options.formData.fileMd5 = fileMd5;
            return deferred.promise();
        },
        afterSendFile: function () {
            $.post('<?=Url::to(['video-upload/merge-chunks'])?>', {
                fileMd5: fileMd5,
            }, function (res) {
                $('#videoupload-url').val(res.path);
                $('#message').text(res.message);
            }, 'json');
        }
    });

    var uploader = new WebUploader.Uploader({
        // 自动上传。
        auto: false,
        // swf文件路径
        swf: '/webuploader/Uploader.swf',
        // 文件接收服务端。
        server: '<?=Url::to(['video-upload/upload'])?>',
        sendAsBinary: false,
        method: 'POST',
        pick: '#upload',
        // 只允许选择MP4文件
        accept: {
            extensions: 'mp4',
            mimeTypes: 'video/mp4'
        },
        resize: false,
        chunked: true,   //开启分片上传
        chunkSize: 2 * 1024 * 1024,   //分片大小2M
        threads: 1,
        //单个文件的大小 50M
        fileSingleSizeLimit: 50 * 1024 * 1024
    });

    uploader.on('fileQueued', function (file) {
        //计算文件的唯一标记fileMd5,用于断点续传  如果.md5File(file)方法里只写一个file参数则计算MD5值会很慢 所以加了后面的参数:50*1024*1024,此处的大小取决于配置中的fileSingleSizeLimit
        (new WebUploader.Uploader()).md5File(file, 0, 50 * 1024 * 1024).progress(function (percentage) {
        }).then(function (val) {
            $('#message').text("成功获取文件信息!");
            fileMd5 = val;
            //检查文件上传进度
            $.post('<?=Url::to(['video-upload/check-file'])?>', {
                fileMd5: fileMd5,
            }, function (res) {
                if (res.code === 200) {
                    $(".progress-bar").css('width', res.percent);
                    $(".progress-bar").text(res.percent);
                    if (res.percent == '0%') {
                        $("#btn").text('开始上传');
                    } else if (res.percent == '100%') {
                        $("#btn").text('上传完毕');
                    } else {
                        $("#btn").text('开始上传');
                    }
                }
            }, 'json');
        });
        uploader.stop(true);
    });

    uploader.on('uploadProgress', function (file, percentage) {
        //动态更改上传进度
        var percent = Math.round(percentage * 100) + '%';
        $(".progress-bar").css('width', percent);
        $(".progress-bar").text(percent);
        $("#message").val('上传中...');
    });

    uploader.on('error', function (error) {
        //错误处理
        console.log(error);
        $("#btn").val('开始上传');
        uploader.stop(true);
        if (error == 'F_EXCEED_SIZE') {
            $('#message').text('文件最大限制为50M');
        } else {
            $('#message').text(error);
        }
    });

    uploader.on('all', function (type) {
        //实时监听上传状态
        if (type === 'startUpload') {
            state = 'uploading';
        } else if (type === 'stopUpload') {
            state = 'paused';
        } else if (type === 'uploadFinished') {
            state = 'done';
        } else if (type === 'fileQueued') {
            state = 'queued';
        }

        if (state === 'uploading') {
            $("#btn").text('暂停上传');
        } else if (state === 'paused') {
            $("#btn").text('继续上传');
        } else if (state === 'queued') {
            $("#btn").removeAttr('disabled');
        } else if (state === 'done') {
            $("#btn").text('上传完毕');
            $("#btn").attr('disabled', 'disabled');
        }
    });

    uploader.on('uploadSuccess', function (file, response) {
        $(".progress-bar").css('width', '100%');
        $(".progress-bar").text('100%');
        $("#btn").text('上传完毕');
        $("#btn").attr('disabled', 'disabled');
    });

    //手动操作视频上传/暂停/继续
    $("#btn").click(function () {
        if (state === 'uploading') {
            uploader.stop(true);
            return false;
        }
        uploader.upload(fileObj);
    });
</script>

后端接口代码

/**
 * 分片上传
 * @return array
 */
public function actionUpload()
{
    if (Yii::$app->request->isPost) {
        Yii::$app->response->format = Response::FORMAT_JSON;
        $file = UploadedFile::getInstanceByName('file');
        $chunk = Yii::$app->request->post('chunk', 0);
        $fileMd5 = Yii::$app->request->post('fileMd5');
        $chunks = Yii::$app->request->post('chunks', 1);
        $percent = ((round(($chunk + 1) / $chunks, 2)) * 100) . '%';
        //记录该文件的上传进度
        Yii::$app->cache->set($fileMd5, $percent);
        //把分片内容按照一定规律存储
        rename($file->tempName, $this->tempFile($fileMd5, $chunk));
        return [
            'code' => 200,
            'message' => 'success',
        ];
    }
}

/**
 * 合并所有分片
 * @return array
 */
public function actionMergeChunks()
{
    $fileMd5 = Yii::$app->request->post('fileMd5');
    $full = $this->tempFile($fileMd5);
    $fp = fopen($full, 'wb');
    $i = 0;
    while (file_exists($this->tempFile($fileMd5, $i))) {
        $content = file_get_contents($this->tempFile($fileMd5, $i));
        fwrite($fp, $content);
        //删除分片
        unlink($this->tempFile($fileMd5, $i));
        $i++;
    }
    fclose($fp);
    Yii::$app->cache->delete($fileMd5);
    Yii::$app->response->format = Response::FORMAT_JSON;
    $data = [
        'code' => 200,
        'message' => 'success',
        'path' => '../upload/' . $fileMd5 . '.mp4',
    ];
    return $data;
}

/**
 * 分片上传前检查分片是否已存在
 */
public function actionCheckChunk()
{
    if (Yii::$app->request->isPost) {
        Yii::$app->response->format = Response::FORMAT_JSON;
        $fileMd5 = Yii::$app->request->post('fileMd5');
        $chunk = Yii::$app->request->post('chunk', 0);
        if (file_exists($this->tempFile($fileMd5, $chunk))) {
            return [
                'code' => 0,
                'message' => 'chunk_exists',
            ];
        } else {
            return [
                'code' => 1,
                'message' => 'chunk_not_exists',
            ];
        }
    }
}

/**
 * 开始上传前检查文件上传进度
 * @return array
 */
public function actionCheckFile()
{
    Yii::$app->response->format = Response::FORMAT_JSON;
    $fileMd5 = Yii::$app->request->post('fileMd5');
    $percent = Yii::$app->cache->get($fileMd5) ? Yii::$app->cache->get($fileMd5) : '0%';
    return [
        'code' => 200,
        'percent' => $percent,
    ];
}

/**
 * 获取格式化的文件名
 * @param $fileMd5
 * @param null $chunk
 * @return bool|string
 */
private function tempFile($fileMd5, $chunk = null)
{
    $tempFile = Yii::getAlias('@webroot/upload/' . $fileMd5);
    if (is_null($chunk)) {
        $tempFile .= '.mp4';
    } else {
        $tempFile .= '.chunk' . $chunk;
    }
    return $tempFile;
}

参考文章:http://blog.ncmem.com/wordpress/2023/11/17/webuploader%e5%ae%9e%e7%8e%b0%e5%a4%a7%e6%96%87%e4%bb%b6%e6%96%ad%e7%82%b9%e7%bb%ad%e4%bc%a0/
欢迎入群一起讨论

在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值