准备工作

1.在.env文件中添加以下配置:

  1. #七牛云存储需要华南区
  2. QN_YUMING=qn3.dzbfsj.com
  3. QN_ACCESS_KEY=mr7yBWpG
  4. QN_SECRET_KEY=rIZTtUBm3tNCSz
  5. QN_BUCKET=kzhtest
  6. QN_REGION=SCN
  7. QN_ACCESS=public
  8. QN_NOTIFY_URL=
  9. QN_FILE_URL=http://qn3.dzbfsj.com/

2. 安装zgldh/qiniu-laravel-storage

composer require zgldh/qiniu-laravel-storage

3.在config/filesystem.php 里面的 disks数组加上:

        'qiniu' => [
            'driver'  => 'qiniu',
            'domains' => [
                'default'   => env('QN_YUMING'),//你的七牛域名
                'https'     => env('QN_HTTPS'),//你的HTTPS域名
                'custom'    => env('QN_ZDYYM'),//你的自定义域名
             ],
            'access_key'=> env('QN_ACCESS_KEY'),  //AccessKey
            'secret_key'=> env('QN_SECRET_KEY'),  //SecretKey
            'bucket'    => env('QN_BUCKET'),  //Bucket名字
            'notify_url'=> env('QN_NOTIFY_URL'),  //持久化处理回调地址
            'access'    => env('QN_ACCESS'),////空间访问控制 public 或 private
            'region'    => env('QN_REGION', 'SCN'),  //地区//ECN华东 NCN华北 SCN华南 NA北美 ASG东南亚
            'url'       => env('QN_FILE_URL'), //填写文件访问根url:http://of8kfibjo.bkt.clouddn.com/
        ],

到这一步,dcat admin后台就可以使用自带的form->file上传文件到七牛云了。但是,因上传要经过php中转,先上传到服务器,再从服务器上传到七牛,无法上传大文件,且速度很慢。

我们要使用前端直传,最好能切片上传,省去一个步骤,加快上传速度。以下的我试验过的三种方案:

一、最简方案

该方案适合用来上传指定后缀的文件,比如mp4视频。代码如下:

            $filename = md5(uniqid()) . '.mp4';// 随机文件名
            $disk     = Storage::disk('qiniu');
            // 回调信息, Dcat 上传文件需要的报文格式
            $callBack = [
                'status' => true,
                'data'   => [
                    'id'   => $filename,
                    'name' => $filename,
                    'path' => $filename,
                    'url'  => config('filesystems.disks.qiniu.url') . $filename,
                ],
            ];
            $policy = [
                'returnBody' => json_encode($callBack),
            ];
            // 七牛云上传 token
            $token = $disk->uploadToken($filename, 3600, $policy);
            $form->file('video')->disk('qiniu')->accept('mp4')->autoUpload()->help('仅支持MP4格式')->maxSize(5120000)->removable(false)->url('https://up-cn-east-2.qiniup.com')->options(['fileVal' => 'file'])->on('startUpload', <<<JS
                function () {
                    // 上传文件前附加自定义参数到文件上传接口
                    const config = {
                        chunkSize: 5,//切片5MB
                        forceDirect: true,//全部采用直传
                    };
                    this.uploader.options.formData['token'] = "{$token}";
                    this.uploader.options.formData['fileName'] = "{$filename}";
                    this.uploader.options.formData['key'] = "{$filename}";
                    this.uploader.options.formData['config'] = config;
                }
JS
            );

该方案充分利用了dcat自身的上传功能,不足之处是,只适合上传固定类型的文件,且不支持切片上传,大文件还是容易超时中止上传。

二、扩展表单字段方案

1.新建组件类 app/Admin/Extensions/Form/qnupload.php

<?php

namespace App\Admin\Extensions\Form;

use Dcat\Admin\Form\Field;

class qnupload extends Field
{
    protected $view = 'qnupload';
}

2.新建视图文件 resources/views/admin/qnupload.blade.php:

<div class="{{$viewClass['form-group']}}">

    <label class="{{$viewClass['label']}} control-label">{{$label}}</label>

    <div class="{{$viewClass['field']}}">

        @include('admin::form.error')

        <div {!! $attributes !!} style="width: 100%; height: 100%;">
            <p>{!! $value !!}</p>

                <div class="col-md-12">
                    <div id="container">
                        <a class="btn btn-default btn-lg " id="pickfiles" href="#" >
                            <i class="glyphicon glyphicon-plus"></i>
                            <span>选择文件</span>
                        </a>
                        <a class="btn btn-default btn-lg " id="up_load"  href="#" >
                            <span>确认上传</span>
                        </a>
                        <a class="btn btn-default btn-lg " id="stop_load"  href="#" >
                            <span>暂停上传</span>
                        </a>
                        <a class="btn btn-default btn-lg " id="retry"  href="#" >
                            <span>重试</span>
                        </a>
                    </div>
                </div>
                <div style="display:none" id="success" class="col-md-12">
                    <div class="alert-success">
                        队列全部文件处理完毕
                    </div>
                </div>
                <div class="col-md-12 ">
                    <table class="table table-striped table-hover text-left"   style="margin-top:40px;display:none">
                        <thead>
                        <tr>
                            <th style="width:50%">文件名</th>
                            <th style="width:20%">大小</th>
                            <th style="width:30%">进度</th>
                        </tr>
                        </thead>
                        <tbody id="fsUploadProgress"></tbody>
                    </table>
                </div>
        </div>
        <div class="qnfile">
            <input type="hidden" name="{{$name}}" value="{{ old($column, $value) }}" />
        </div>
        @include('admin::form.help-block')
    </div>
</div>

<!-- script标签加上 "init" 属性后会自动使用 Dcat.init() 方法动态监听元素生成 -->
<script require="@qnupload" init="{!! $selector !!}">
    // id 变量是 Dcat.init() 自动生成的,是一个唯一的随机字符串
    var uploader = Qiniu.uploader({
        disable_statistics_report: true,
        makeLogFunc: 1,
        runtimes: 'html5,flash,html4',
        browse_button: 'pickfiles',
        container: 'container',
        drop_element: 'container',
        max_file_size: '10000mb',
        dragdrop: true,
        chunk_size: '4mb',
        multi_selection: !(moxie.core.utils.Env.OS.toLowerCase()==="ios"),
        uptoken: "{{\Storage::disk('qiniu')->uploadToken()}}",
        unique_names: true,
        max_retries: 7, // 上传失败最大重试次数
        domain: "{{config('filesystems.disks.qiniu.url')}}",
        get_new_uptoken: true,
        auto_start: true,
        log_level: 5,
        init: {
            'BeforeChunkUpload':function (up,file) {
                console.log("分块上传文件:",file.name);
            },
            'FilesAdded': function(up, files) {
                $('table').show();
                $('#success').hide();
                plupload.each(files, function(file) {
                    console.log('文件类型: ' + file.type);
                    if(file.type=='image/jpeg'||file.type=='image/jpg'||file.type=='image/png'||file.type=='image/gif' || file.type=='video/x-matroska' || file.type=='video/mp4'){
                        isUpload =true;
                        // file.album_name=album_name;
                        var progress = new FileProgress(file, 'fsUploadProgress');
                        progress.setStatus("等待...");
                        progress.bindUploadCancel(up);
                    }else {
                        isUpload = false;
                        up.removeFile(file);
                        console.log('上传类型只能是.jpg,.png,.gif,.mp4');
                        return false;
                    }});
            },
            'BeforeUpload': function(up, file) {
                console.log("开始上传BeforeUpload");
                var progress = new FileProgress(file, 'fsUploadProgress');
                var chunk_size = plupload.parseSize(this.getOption('chunk_size'));
                if (up.runtime === 'html5' && chunk_size) {
                    progress.setChunkProgess(chunk_size);
                }
            },
            'UploadProgress': function(up, file) {
                var progress = new FileProgress(file, 'fsUploadProgress');
                var chunk_size = plupload.parseSize(this.getOption('chunk_size'));
                progress.setProgress(file.percent + "%", file.speed, chunk_size);
            },
            'UploadComplete': function() {
                $('#success').show();
            },
            'FileUploaded': function(up, file, info) {
                var progress = new FileProgress(file, 'fsUploadProgress');
                progress.setComplete(up, info);
            },
            'Error': function(up, err, errTip) {
                $('table').show();
                var progress = new FileProgress(err.file, 'fsUploadProgress');
                progress.setError();
                progress.setStatus(errTip);
            }

            // ,
            // 'Key': function(up, file) {
            //     var key = "";
            //     // do something with key
            //     return key
            // }
        }
    });
    uploader.bind('FilesAdded', function() {
        console.log("添加了一个文件");
    });
    uploader.bind('BeforeUpload', function () {
        console.log("即将上传");
    });
    uploader.bind('FileUploaded', function () {
        console.log('已上传一个文件');
    });
    $('#up_load').on('click', function(){
        uploader.start();
    });
    $('#stop_load').on('click', function(){
        uploader.stop();
    });
    $('#retry').on('click', function(){
        uploader.stop();
        uploader.start();
    });
    $('#container').on(
        'dragenter',
        function(e) {
            e.preventDefault();
            $('#container').addClass('draging');
            e.stopPropagation();
        }
    ).on('drop', function(e) {
        e.preventDefault();
        $('#container').removeClass('draging');
        e.stopPropagation();
    }).on('dragleave', function(e) {
        e.preventDefault();
        $('#container').removeClass('draging');
        e.stopPropagation();
    }).on('dragover', function(e) {
        e.preventDefault();
        $('#container').addClass('draging');
        e.stopPropagation();
    });


    $('body').on('click', 'table button.btn', function() {
        $(this).parents('tr').next().toggle();
    });


    var getRotate = function(url) {
        if (!url) {
            return 0;
        }
        var arr = url.split('/');
        for (var i = 0, len = arr.length; i < len; i++) {
            if (arr[i] === 'rotate') {
                return parseInt(arr[i + 1], 10);
            }
        }
        return 0;
    };
</script>

3. 然后注册进 dcat-admin,在 app/Admin/bootstrap.php 中添加以下代码:

    // 七牛切片直传
    Admin::asset()->alias('@qnupload', [
        'js' => [
            '/js/qiniu/ui.js',
            '/js/qiniu/dist/qiniu.js'
        ],
        'css' => '/css/main.css',
    ]);
    Form::extend('qnfile', qnupload::class);

相关的js,css文件可在七牛官网的demo中找到。

使用方法:$form->qnfile(‘video’);

该方法初步测试,可以实现大文件前端直传,切片上传也正常,不过使用不方便,ui也较难看。

三、扩展dcat自带上传组件Field

1. 在app/Admin/Extensions/Field新建一个File.php: