记录一下自己写的PHP大文件分段上传代码,方面以后要用的时候直接复制粘贴。使用了Layui、JQuery和ThinkPHP,还有一些优化空间,等下次用到的时候再完善~

样式

<style>
.input-toolbar {
    display: inline-block;
    position: absolute;
    top: 1px;
    right: 1px;
    height: 36px;
    line-height: 36px;
    border-radius: 0 2px 2px 0;
    border-left: 1px solid #e6e6e6;
    background-color: #eee;
    font-size: 0;
}
.input-toolbar .layui-icon {
    width: 36px;
    height: 36px;
    display: inline-block;
    text-align: center;
    border: 0;
    border-right: 1px solid #e6e6e6;
    cursor: pointer;
}
.input-toolbar .layui-icon:hover {
    background-color: #e0e0e0;
}
.input-toolbar .layui-icon:last-child {
    border-right: 0;
}
.video-upload {
    position: absolute;
    opacity: 0;
    top: 0;
    display: block;
    width: 36px;
    height: 36px;
}
</style>

HTML

<div class="layui-form-item" id="video-container">
    <label class="layui-form-label">视频文件</label>
    <div class="layui-input-block">
        <input type="text" name="video" class="layui-input" placeholder="请在右侧上传视频">
        <div class="input-toolbar">
            <button type="button" class="layui-icon layui-icon-upload" title="点击上传">
                <input type="file" class="video-upload" data-bind="[name='video']" data-progress="videoProgress">
            </button>
            <button type="button" class="layui-icon video-preview" data-bind="[name='video']" title="点击查看预览">&#xe64a;</button>
        </div>
        <br/>
        <div class="layui-progress layui-progress-big" lay-filter="videoProgress" lay-showPercent="true">
            <div class="layui-progress-bar" lay-percent="0%"></div>
        </div>
    </div>
</div>

Javascript

layui.use(['jquery', 'layer', 'element'], function(){
    let $ = layui.jquery
        ,layer = layui.layer
        ,element = layui.element;
    
    // 图片预览
    $('.preview').click(function () {
        var bind = $(this).data('bind');
        if (!bind) {
            layer.msg('缺少属性: data-bind');
            return ;
        }

        var src = $(bind).val();
        if (!src) {
            layer.msg('图片地址为空');
            return ;
        }

        layer.photos({
            photos: {
                "title": "预览",
                "data": [{"src": src}]
            }
        });
    });

    $('.video-upload').change(async function () {
        let that = this;
        $(this).prop('disabled', true);
        element.progress(that.dataset.progress, '0%');

        console.log(that)

        try {
            if (!that.files.length) {
                throw new Error('请选择文件');
            }

            let chunkSize = 1 * 1024 * 1024
                ,blob = that.files[0]
                ,chunkTotal = Math.ceil(blob.size / chunkSize)
                ,start = 0
                ,progress = 0
                ,end, formData;

            if (!/.mp4$/i.test(blob.name)) {
                throw new Error('仅允许上传mp4格式的视频');
            }

            for (var i = 1; i <= chunkTotal; i++) {
                if (i == chunkTotal) {
                    end = blob.size;
                } else {
                    end = start + chunkSize;
                }

                formData = new FormData();
                formData.append("file", blob.slice(start, end), blob.name);
                formData.append("batch", i);
                await fetch("{:url('videoUpload')}", {
                    body: formData
                    ,method: 'POST'
                    ,credentials: 'same-origin'
                })
                    .then(response => response.json())
                    .then(function (result) {
                        progress = i / (chunkTotal + 1);
                        element.progress(that.dataset.progress, progress * 100 + '%');
                    })
                    .catch(function (error) {
                        throw new Error(error);
                    });

                start = end;

            }

            formData = new FormData();
            formData.append("filename", blob.name);
            formData.append("success", 1);
            await fetch("{:url('videoUpload')}", {
                body: formData
                ,method: 'POST'
                ,credentials: 'same-origin'
            })
                .then(response => response.json())
                .then(function (result) {
                    $(that.dataset.bind).val(result.data.src);
                })
                .catch(function (error) {
                    throw new Error(error);
                });

            progress = 1;
            element.progress(that.dataset.progress, progress * 100 + '%');
        } catch (e) {
            layer.alert(e.message);
        }

        $(this).prop('disabled', false);
    });

    // 视频预览
    $('.video-preview').click(function () {
        var bind = $(this).data('bind');
        if (!bind) {
            layer.msg('缺少属性: data-bind');
            return ;
        }

        var src = $(bind).val();
        if (!src) {
            layer.msg('视频地址为空');
            return ;
        }

        layer.open({
            type: 1,
            title: '视频预览',
            area: ['450px', '400px'],
            content: '<video src="' + src + '" controls="controls" style="width: 100%;"></video>'
        });
    });
});

PHP ThinkPHP控制器方法代码

public function videoUpload()
{
    if (Request::has('success', 'param', true)) {
        $tmpDir = 'storage/article_tmp/';
        $videoDir = 'storage/article_video/';

        $filenameMd5 = md5(Request::param('filename'));
        $tmpFilePath = $tmpDir . $filenameMd5 . '.' . pathinfo(Request::param('filename'), PATHINFO_EXTENSION);

        $fileResource = fopen($tmpFilePath, 'w+');

        foreach(scandir($tmpDir) as $item) {
            if (0 !== strpos($item, $filenameMd5 . '.mp4_')) continue;
            fwrite($fileResource, file_get_contents($tmpDir . $item));
            unlink($tmpDir . $item);
        }
        fclose($fileResource);

        $newFileName = md5_file($tmpFilePath) . '.mp4';
        $newFilePath = $videoDir . $newFileName;
        if (!file_exists($newFilePath)) {
            rename($tmpFilePath, $newFilePath);
        } else {
            unlink($tmpFilePath);
        }

        return json([
            'code' => 0,
            'msg'  => '上传成功',
            'data' => [
                'src'  => '/storage/article_video/' . $newFileName,
                'size' => filesize($newFilePath),
            ]
        ]);
    } else {
        try {
            $file = Request::file('file');
            if (null === $file) {
                throw new \Exception('请上传文件', UPLOAD_ERR_NO_FILE);
            }

            validate(['file' => [
                'fileSize' => Config::get('filesystem.maxFileSize'),
                'fileExt'  => 'mp4',
            ]])->check(['file' => $file]);

            $name = md5($file->getOriginalName()) . '.' . $file->extension() . '_' . Request::param('batch', 1);
            Filesystem::disk('public')->putFileAs('article_tmp', $file, $name);
        } catch (\Exception $e) {
            return json([
                'code' => 1,
                'msg'  => $e->getMessage(),
            ]);
        }

        return json([
            'code' => 0,
            'msg'  => '上传成功',
        ]);
    }
}

标签: none

添加新评论