其他分享
首页 > 其他分享> > laravel + webupload + 阿里云OSS直传分享

laravel + webupload + 阿里云OSS直传分享

作者:互联网

最近在做管理后天时,要上传一些app包文件,这些包大小都在几百兆内,普通上传,对服务器压力不小,而且很耗服务器资源。后来发现阿里云可以做web直传,所以,今天我就来记录下过程。

附: 阿里云web直传OSS文档
阿里云OSS直传示例(php版)

这示例文档已经说得很清楚,你唯一要做的就是下载PHP示例包,继承到自己项目根目录中,测试下示例是否能上传文件到oss,然后再想办法集成到webupload插件中。至于阿里云的appkey 和Appsecret,最好是放在后端代码中,不要放在前端,不安全,也即是后端签名直传。

代码:

源包: aliyun-oss-appserver-php-master
包目录

        'oss' => [
            'driver'        => 'oss',
            'access_id'     => 'xxxx',
            'access_key'    => 'xxxxxxxxxx',
            'bucket'        => 'xxxxxx',
            'endpoint'      => 'oss-cn-shenzhen.aliyuncs.com/', // OSS 外网节点或自定义外部域名
            //'endpoint_internal' => '<internal endpoint [OSS内网节点] 如:oss-cn-shenzhen-internal.aliyuncs.com>', // v2.0.4 新增配置属性,如果为空,则默认使用 endpoint 配置(由于内网上传有点小问题未解决,请大家暂时不要使用内网节点上传,正在与阿里技术沟通中)
//            'cdnDomain'     => 'http://xxxxx.xxx.com', // 如果isCName为true, getUrl会判断cdnDomain是否设定来决定返回的url,如果cdnDomain未设置,则使用endpoint来生成url,否则使用cdn
            'ssl'           => false ,// true to use 'https://' and false to use 'http://'. default is false,
//            'isCName'       => true ,// 是否使用自定义域名,true: 则Storage.url()会使用自定义的cdn或域名生成文件url, false: 则使用外部节点生成url
            'debug'         => env("APP_DEBUG",false),
            'juta_apk' => env("JUTA_APK","upload"),//apk地址
        ],

配置文件

把 get.php文件内容拷贝到项目控制器,或其他目录文件中,我这里是在Models层多加了一层Service层,所以我放在/app/Models/Service/UploadService.php文件中,对应配置替换成自己阿里云的配置

    public function gmtIso8601($time) {
        $dtStr = date("c", $time);
        $mydatetime = new \DateTime($dtStr);
        $expiration = $mydatetime->format(\DateTime::ISO8601);
        $pos = strpos($expiration, '+');
        $expiration = substr($expiration, 0, $pos);
        return $expiration."Z";
    }
    public function getParams($dir){
        $id= config("filesystems.disks.oss.access_id");          // 请填写您的AccessKeyId。
        $key= config("filesystems.disks.oss.access_key");     // 请填写您的AccessKeySecret。
        // $host的格式为 bucketname.endpoint,请替换为您的真实信息。
//        $host = 'http://bucket-name.oss-cn-hangzhou.aliyuncs.com';
        $host = "http://".config("filesystems.disks.oss.bucket").'.'.config("filesystems.disks.oss.endpoint");
        // $callbackUrl为上传回调服务器的URL,请将下面的IP和Port配置为您自己的真实URL信息。
        $callbackUrl = 'http://'.$_SERVER['HTTP_HOST'].'/upload/callBack';
//        $dir = 'user-dir-prefix/';          // 用户上传文件时指定的前缀。
//        $dir = $dir;          // 用户上传文件时指定的前缀。

        $callback_param = array('callbackUrl'=>$callbackUrl,
            'callbackBody'=>'filename=${object}&size=${size}&mimeType=${mimeType}&height=${imageInfo.height}&width=${imageInfo.width}',
            'callbackBodyType'=>"application/x-www-form-urlencoded");
        $callback_string = json_encode($callback_param);

        $base64_callback_body = base64_encode($callback_string);
        $now = time();
        $expire = 30;  //设置该policy超时时间是10s. 即这个policy过了这个有效时间,将不能访问。
        $end = $now + $expire;
        $expiration = $this->gmtIso8601($end);


        //最大文件大小.用户可以自己设置
        $condition = array(0=>'content-length-range', 1=>0, 2=>1048576000);
        $conditions[] = $condition;

        // 表示用户上传的数据,必须是以$dir开始,不然上传会失败,这一步不是必须项,只是为了安全起见,防止用户通过policy上传到别人的目录。
        $start = array(0=>'starts-with', 1=>'$key', 2=>$dir);
        $conditions[] = $start;


        $arr = array('expiration'=>$expiration,'conditions'=>$conditions);
        $policy = json_encode($arr);
        $base64_policy = base64_encode($policy);
        $string_to_sign = $base64_policy;
        $signature = base64_encode(hash_hmac('sha1', $string_to_sign, $key, true));

        $response = array();
        $response['accessid'] = $id;
        $response['host'] = $host;
        $response['policy'] = $base64_policy;
        $response['signature'] = $signature;
        $response['expire'] = $end;
        $response['callback'] = $base64_callback_body;
        $response['dir'] = $dir;  // 这个参数是设置用户上传文件时指定的前缀。
        echo json_encode($response);
    }

回调代码放在/app/Http/Controllers/UploadController.php文件中

<?php
namespace App\Http\Controllers;

use App\Models\Service\UploadService;
use Illuminate\Http\Request;
use DB;

class UploadController extends Controller
{
    public function getUploadSign(Request $request){
        $remoteDir = $request->get("dir");
        $uploadService = new UploadService();
        $res = $uploadService->getParams($remoteDir);
        return $res ;
    }
    public function callBack(){
        // 1.获取OSS的签名header和公钥url header
        $authorizationBase64 = "";
        $pubKeyUrlBase64 = "";
        /**
         * 注意:如果要使用HTTP_AUTHORIZATION头,你需要先在apache或者nginx中设置rewrite,以apache为例,修改
         * 配置文件/etc/httpd/conf/httpd.conf(以你的apache安装路径为准),在DirectoryIndex index.php这行下面增加以下两行
            RewriteEngine On
            RewriteRule .* - [env=HTTP_AUTHORIZATION:%{HTTP:Authorization},last]
         * */
        if (isset($_SERVER['HTTP_AUTHORIZATION']))
        {
            $authorizationBase64 = $_SERVER['HTTP_AUTHORIZATION'];
        }
        if (isset($_SERVER['HTTP_X_OSS_PUB_KEY_URL']))
        {
            $pubKeyUrlBase64 = $_SERVER['HTTP_X_OSS_PUB_KEY_URL'];
        }

        if ($authorizationBase64 == '' || $pubKeyUrlBase64 == '')
        {
            header("http/1.1 403 Forbidden");
            exit();
        }

        // 2.获取OSS的签名
        $authorization = base64_decode($authorizationBase64);

        // 3.获取公钥
        $pubKeyUrl = base64_decode($pubKeyUrlBase64);
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $pubKeyUrl);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
        curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 10);
        $pubKey = curl_exec($ch);
        if ($pubKey == "")
        {
            //header("http/1.1 403 Forbidden");
            exit();
        }

        // 4.获取回调body
        $body = file_get_contents('php://input');

        // 5.拼接待签名字符串
        $authStr = '';
        $path = $_SERVER['REQUEST_URI'];
        $pos = strpos($path, '?');
        if ($pos === false)
        {
            $authStr = urldecode($path)."\n".$body;
        } else {
            $authStr = urldecode(substr($path, 0, $pos)).substr($path, $pos, strlen($path) - $pos)."\n".$body;
        }

        // 6.验证签名
        $ok = openssl_verify($authStr, $authorization, $pubKey, OPENSSL_ALGO_MD5);
        if ($ok == 1) {
            header("Content-Type: application/json");
            $data = array("Status"=>"Ok");
            echo json_encode($data);
        } else {
            //header("http/1.1 403 Forbidden");
            exit();
        }

    }
}
//web端直传oss
Route::post("upload/getUploadSign","UploadController@getUploadSign");
Route::post("upload/callBack","UploadController@callBack");
	注:回调地址一定要去token验证,在app/Middleware/VerifyCsrfToken.php文件中
<?php

namespace App\Http\Middleware;

use Illuminate\Foundation\Http\Middleware\VerifyCsrfToken as Middleware;

class VerifyCsrfToken extends Middleware
{
    /**
     * The URIs that should be excluded from CSRF verification.
     *
     * @var array
     *
     */
    protected $except = [
        "upload/callBack"   //web直传签名回调
    ];
}

<div class="form-group">
    <label for="download_url" class="control-label col-sm-2"><span class="require_red">*&nbsp;&nbsp;</span>上传apk:</label>
    <div id="uploader" class="wu-example col-sm-10 col-md-4 col-lg-5">
        <input type="hidden" name="file"> <!--这里一定的加上 name=‘file’ 否侧就报错-->
        <!--用来存放文件信息-->
        <div id="preview" class="uploader-list" style="margin-bottom: 3px"></div>
        <div id="picker" class="col-sm-5  col-md-3">选择文件</div>
        <div id="ctlBtn" class="btn btn-primary" style="height: 40px">开始上传</div>
          <span class="require_red font-size">文件类型xxx.apk</span>
    </div>
</div>
                        
    
   <script src="/plugins/select-input-autocomplete/js/input-autocomplete.js" type="text/javascript"></script>
    <script src="{{asset('plugins')}}/webuploader-0.1.5/webuploader.min.js"></script>
    <script type="text/javascript" src="{{asset('plugins')}}/aliyun-oss-appserver-php-master/upload.js"></script>
    <script>
        var objdata = {
            upfile_nametype:"local_name",
            cdn_url : 'https://staticwxappt.loke123.com/',
            host :  "{{"https://".config("filesystems.disks.oss.bucket").".".config("filesystems.disks.oss.endpoint")}}",
            dir : "{{config("filesystems.disks.oss.juta_apk")."/"}}",
            path : ''
        };
        var uploader = WebUploader.create({
            auto:false,

            // swf文件路径
            swf: "{{ asset("plugins") }}/webuploader-0.1.5/Uploader.swf",

            // 文件接收服务端。
            server: objdata.host,//阿里云oss地址

            // 选择文件的按钮。可选。
            // 内部根据当前运行是创建,可能是input元素,也可能是flash.
            pick: '#picker',
            // accept:{
            //     title:'Images',
            //     extentions: "png,jpeg,jpg",
            //     mimeTypes: 'image/jpg,image/jpeg,image/png'
            // },
           //{{--formData:{--}}
            //        {{--'_token':'{{csrf_token()}}'--}}
            //        {{--},--}}
            fileVal:"file",
            // 不压缩image, 默认如果是jpeg,文件上传前会压缩一把再上传!
            resize: false,
            fileNumLimit: 1
        });
        uploader.on("uploadBeforeSend",function (obj, data, headers) {
            $.ajax({
                type:"post",
                url:"/upload/getUploadSign",
                timeout:10000,
                data:{
                    "_token": '{{csrf_token()}}',
                    'dir' : objdata.dir
                },
                success : function (str) {
                    console.log("str",str);
                    if  (str) {
                        try{
                            var res = JSON.parse(str);
                            console.log('res',res)
                            // console.log('ll',calculate_object_name(data.name,objdata.upfile_nametype));return;
                            $.extend(data,{
                                'key' : res.dir + calculate_object_name(data.name,objdata.upfile_nametype),
                                'policy': res.policy,
                                'OSSAccessKeyId': res.accessid,
                                'success_action_status' : '200', //让服务端返回200,不然,默认会返回204  
                                'callback' : res.callback,
                                'signature': res.signature
                            });
                        } catch (e) {
                            layer.alert("系统错误");
                        }

                    } else {
                        layer.alert("结果为空");
                    }
                },
                error : function(XMLHttpRequest, textStatus, errorThrown) {
                    alert("ajax error");
                },
                complete : function(XMLHttpRequest,status){ //请求完成后最终执行参数  
                    if(status == 'timeout'){
                        layer.alert('请求超时,请稍后再试!');
                    }
                },
                async : false
            })
            obj.filepath = objdata.cdn_url + data.key;
            objdata.path = objdata.cdn_url + data.key;
            console.log("data",data)
            headers['Access-Control-Allow-Origin'] = "*";
        })
        // 当有文件添加进来的时候
        uploader.on( 'fileQueued', function( file ) {
            // 创建缩略图
            // 如果为非图片文件,可以不用调用此方法。
            // uploader.makeThumb( file, function( error, src ) {
            //     if ( error ) {
            //         layer.msg('不能预览');
            //         return;
            //     }
            //     $("#preview").html("<img src='"+src+"'>");
            // }, 100, 100 );
            //删除错误的第一个文件
            $("#picker").on("click",function () {
                uploader.removeFile(file);
            })
        });
        //点击上传
        $("#ctlBtn").on("click",function (obj) {
            layer.load();
            uploader.upload();
        })
        // 文件上传成功,给item添加成功class, 用样式标记上传成功。
        uploader.on( 'uploadSuccess', function( file , msg ) {
            layer.closeAll()
            if (msg.Status == 'Ok') {
                layer.msg('上传成功',{icon:1,time:2000});
                $('input[name=download_url]').val(objdata.path);
            } else {
                layer.msg("上传失败",{icon:2,time:3000});
            }
            console.log("file",file);
            console.log("msg",msg);
        });
        // 文件上传失败,显示上传出错。
        uploader.on( 'uploadError', function( file ) {
            layer.closeAll()
            // console.log("error file",file);
            var $li = $( '#'+file.id ),
                $error = $li.find('div.error');

            // 避免重复创建
            if ( !$error.length ) {
                $error = $('<div class="error"></div>').appendTo( $li );
            }

            $error.text('上传失败');
        });
    </script>

function random_string(len) {
  len = len || 32;
  var chars = 'ABCDEFGHJKMNPQRSTWXYZabcdefhijkmnprstwxyz2345678';   
  var maxPos = chars.length;
  var pwd = '';
  for (i = 0; i < len; i++) {
      pwd += chars.charAt(Math.floor(Math.random() * maxPos));
    }
    return pwd;
}

function get_suffix(filename) {
    pos = filename.lastIndexOf('.')
    suffix = ''
    if (pos != -1) {
        suffix = filename.substring(pos)
    }
    return suffix;
}
//获取文件名
function calculate_object_name(filename,g_object_name_type) {
    var g_object_name = '' ;
    if (g_object_name_type == 'local_name') {
        g_object_name = filename
    } else if (g_object_name_type == 'random_name') {
        suffix = get_suffix(filename)
        // g_object_name = key + random_string(10) + suffix
        g_object_name = random_string(16) + suffix
    }
    return g_object_name ;
}

至此,已完成了laravel+ webupload+OSS 直传,如有疑问,请留言!

标签:laravel,function,name,OSS,dir,file,上传,oss,直传
来源: https://blog.csdn.net/qq_35770969/article/details/99820454