1. 程式人生 > >laravel 圖片上傳阿里雲oss

laravel 圖片上傳阿里雲oss

表單:

<div class="form-group">
  <label class="col-sm-2 control-label">圖片</label>
  <div class="col-sm-8 media-picker"> 
    <div class="input-group">
      <a href="javascript:void(0)" class="button media-picker-button" data-id="images" id="images_uploader" data-multiple="multiple" style="z-index: 98;"> 
        <span>+</span> 
        <input type="hidden" id="images" name="images" value="" data-oss-path="courses">
      </a>
    </div>
    <p class="help-block">多圖上傳,最多上傳8張</p>
  </div>
</div>

到阿里雲下載需要的例項檔案,我是用的服務端簽名zh直傳

把前端檔案拷貝下,如我把plupload拷貝到了public/plugins/下,把其他upload.js和.upload.js.swpkao拷貝到了public/aliyunoss下

在laravel的app下新建libs資料夾,並在composer.json中配置autoload

"classmap": [
            "database/seeds",
            "database/factories",
            "app/Libs"
        ],

在libs下新建Aliyunoss.php

把下載下來的例項php整理下

namespace App\Libs;


class Aliyunoss {
  
  private $id;
  private $key;
  private $host;
  public function __construct() 
  {
    $this->id= 'id';
    $this->key= 'key';
    $this->host = 'ossurl';
  }

  public function get_access_info($dir = 'images') 
  {
    $now = time();
    $expire = 30; //設定該policy超時時間是10s. 即這個policy過了這個有效時間,將不能訪問
    $end = $now + $expire;
    $expiration = $this->gmt_iso8601($end);

    $dir = $dir . '/';

    //最大檔案大小.使用者可以自己設定
    $condition = array(0=>'content-length-range', 1=>0, 2=>1048576000);
    $conditions[] = $condition; 

    $key = $this->key;
    //表示使用者上傳的資料,必須是以$dir開始, 不然上傳會失敗,這一步不是必須項,只是為了安全起見,防止使用者通過policy上傳到別人的目錄
    $start = array(0=>'starts-with', 1 => '$key', 2 => $dir);
    $conditions[] = $start; 


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

    $response = array();
    $response['accessid'] = $this->id;
    $response['host'] = $this->host;
    $response['policy'] = $base64_policy;
    $response['signature'] = $signature;
    $response['expire'] = $end;
    //這個引數是設定使用者上傳指定的字首
    $response['dir'] = $dir;
    return json_encode($response);
  }

  private function gmt_iso8601($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";
  }
}

在後臺新建UploadController.php,並建立路由

namespace App\Http\Controllers\Backend;

use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use App\Libs\Uploads;
use App\Libs\Aliyunoss;

class UploadsController extends Controller {
    

    public function aliyun_access($ossdir = 'images') {
      $aliyunoss = new Aliyunoss();
      return $aliyunoss->get_access_info($ossdir);
    }
}

修改前端upload.js檔案

如果只是單傳,直接就可以使用,但實際開發中一個頁面可能會用到很多shan上傳,所以需要整理下js檔案,我參考了網上一些資料


accessid = ''
accesskey = ''
host = ''
policyBase64 = ''
signature = ''
callbackbody = ''
filename = ''
key = ''
expire = 0
g_object_name = ''
g_object_names = new Array() //多圖片上傳保持
g_object_name_type = 'random_name'
now = timestamp = Date.parse(new Date()) / 1000; 
currentUrl = window.location.href;
backendIndex = currentUrl.indexOf('backend');
baseBackend = currentUrl.substr(0, backendIndex);

function send_request(dir)
{
    var xmlhttp = null;
    if (window.XMLHttpRequest)
    {
        xmlhttp=new XMLHttpRequest();
    }
    else if (window.ActiveXObject)
    {
        xmlhttp=new ActiveXObject("Microsoft.XMLHTTP");
    }
  
    if (xmlhttp!=null)
    {
        serverUrl = baseBackend + 'backend/getoss/' + dir
        xmlhttp.open( "GET", serverUrl, false );
        xmlhttp.send( null );
        return xmlhttp.responseText
    }
    else
    {
        alert("Your browser does not support XMLHTTP.");
    }
};


function get_signature(dir)
{
    //可以判斷當前expire是否超過了當前時間,如果超過了當前時間,就重新取一下.3s 做為緩衝
    now = timestamp = Date.parse(new Date()) / 1000; 
    if (expire < now + 3)
    {
        body = send_request(dir)
        var obj = eval ("(" + body + ")")
        host = obj['host']
        policyBase64 = obj['policy']
        accessid = obj['accessid']
        signature = obj['signature']
        expire = parseInt(obj['expire'])
        callbackbody = obj['callback'] 
        key = obj['dir']
        return true;
    }
    return false;
};

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)
{
    suffix = get_suffix(filename)
    g_object_name = key + random_string(20) + suffix
    return ''
}

function set_upload_param(up, filename, ret, dir)
{
    if (ret == false)
    {
        ret = get_signature(dir)
    }
    g_object_name = key;
    if (filename != '') {
        suffix = get_suffix(filename)
        calculate_object_name(filename)
    }
    new_multipart_params = {
        'key' : g_object_name,
        'policy': policyBase64,
        'OSSAccessKeyId': accessid, 
        'success_action_status' : '200', //讓服務端返回200,不然,預設會返回204
        'callback' : callbackbody,
        'signature': signature,
    };

    up.setOption({
        'url': host,
        'multipart_params': new_multipart_params
    });

    up.start();
}

$(document).ready(function() {
    //上傳
  $('.media-picker').each(function() {
    var el = $(this);
    var elbtn = el.find('.media-picker-button');
    var multi_selection = false;
    // 是否多檔案上傳
    if(elbtn.attr('data-multiple') == 'multiple') {
      multi_selection = true;
    }
    // 上傳目錄
    var inputField = el.find('input[type=hidden]');
    // 如果已經有檔案
    var oldFiles = inputField.val();
    if(oldFiles != '') {
      var oldFilesArr =  $.parseJSON(oldFiles);
      for(var x = 0; x < oldFilesArr.length; x++) {
        g_object_names.push(oldFilesArr[x]);
      }
    } 
    var ossdir = inputField.attr('data-oss-path');
    var uploader = new plupload.Uploader({
      runtimes : 'html5,flash,silverlight,html4',
      browse_button : elbtn.attr('data-id') + '_uploader', 
      multi_selection: multi_selection,
      auto_start: true,
      flash_swf_url : '../plupload/js/Moxie.swf',
      silverlight_xap_url : '../plupload/js/Moxie.xap',
      url : 'http://oss.aliyuncs.com',

      filters: {
        mime_types : [ //只允許上傳圖片和zip,rar檔案
        { title : "Image files", extensions : "jpg,jpeg,gif,png,bmp" }, 
        { title : "Video files", extensions : "mp4,3gp" }
        ],
        max_file_size : '10mb', //最大隻能上傳10mb的檔案
        prevent_duplicates : false //不允許選取重複檔案
      },

      init: { 
        PostInit: function() {
          set_upload_param(uploader, '', false, ossdir);
          return false;
        },

        BeforeUpload: function(up, file) {
          set_upload_param(up, file.name, true, ossdir);
        },

        FilesAdded: function(up) {
          up.start(); //選擇完後直接上傳
        },

        FileUploaded: function(up, file, info) {
          if (info.status == 200)
          {
              var file_type = file.type;
              var is_image = file_type.indexOf('image');
              var is_video = file_type.indexOf('video');
              if (multi_selection) {
                // 多檔案轉為json
                g_object_names.push(g_object_name);
                inputField.val(JSON.stringify(g_object_names));
                // 把圖片顯示出來
                var imageHtml = '';
                for(var i = 0; i < g_object_names.length; i++) {
                  if(is_image > -1) {
                    imageHtml += '<li><div class="img-info"><img src="'+ host + '/' + g_object_names[i] +'"/><p>'+ g_object_names[i] +'</p><div class="delete-image"> X </div></div></li>';
                  }
                  if(is_video > -1) {
                    imageHtml += '<li><div class="img-info"><video controls src="'+ host + '/' + g_object_names[i] +'"></video><p>'+ g_object_names[i] +'</p><div class="delete-image"> X </div></div></li>';
                  }  
                }
                el.find('.medias-list ul').html(imageHtml);
               
              } else {
                // 單檔案直接用名稱, 
                inputField.val(g_object_name);
                // 把圖片顯示出來
                var imageHtml = '';
                if(is_image > -1) {
                  imageHtml = '<li><div class="img-info"><img src="'+ host + '/' + g_object_name +'"/><p>'+ g_object_name +'</p><div class="delete-image"> X </div></div></li>';
                }
                if(is_video > -1) {
                  imageHtml = '<li><div class="img-info"><video controls src="'+ host + '/' + g_object_name +'"></video><p>'+ g_object_name +'</p><div class="delete-image"> X </div></div></li>';
                }
                el.find('.medias-list ul').html(imageHtml);
              }
          }
          else
          {
            alter(info.response);
          } 
        },

        Error: function(up, err) {
          if (err.code == -600) {
              alter("\n選擇的檔案太大了,可以根據應用情況,在upload.js 設定一下上傳的最大大小");
          }
          else if (err.code == -601) {
              alter("\n選擇的檔案字尾不對,可以根據應用情況,在upload.js進行設定可允許的上傳檔案型別");
          }
          else if (err.code == -602) {
              alter("\n這個檔案已經上傳過一遍了");
          }
          else 
          {
             alter("\nError xml:" + err.response);
          }
        }
      }
    })
    uploader.init();
  })

  // 刪除
  $('.media-picker').each(function() {
      var el = $(this)
      var inputField = el.find('input[type=hidden]');
      var fileName = inputField.val();
      var elbtn = el.find('.media-picker-button');
      var multi_selection = false;
      // 是否多檔案上傳
      if(elbtn.attr('data-multiple') == 'multiple') {
        multi_selection = true;
      }
      if(multi_selection) {
        el.on('click', '.delete-image', function() {
          var elDel = $(this);
          // 得到filename
          var currentFileName = elDel.prev('p').html();
          // 刪除當前的父級li
          elDel.parent().parent().remove();
          // 重新賦值陣列
          var fileNames = new Array();
          if(g_object_names != '') {
            fileNames = g_object_names;
          } else {
            fileNames =  $.parseJSON(fileName);
          }
          // 去掉陣列中的當前值
          fileNames.pop(currentFileName);
          $.ajaxSetup({
            headers: {
                'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')
            }, 
          });
          $.ajax({
            type: "POST",
            url: baseBackend + "backend/deleteoss",
            data: "filename=" + currentFileName,
            success: function(msg){
              console.log(msg)
            }
           });
          inputField.val( JSON.stringify(fileNames));
        })
      } else {
        el.on('click', '.delete-image', function() {
          // 顯示值為空
          el.find('.medias-list ul').html('');
          inputField.val('');

          // oss刪除
          if(g_object_name != '') {
            fileName = g_object_name;
          }
          $.ajaxSetup({
            headers: {
                'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')
            }, 
          });
          $.ajax({
            type: "POST",
            url: baseBackend + "backend/deleteoss",
            data: "filename=" + fileName,
            success: function(msg){
              console.log(msg)
            }
           });
        })
      }
      
  })
})