本樣本講解如何在服務端通過PHP程式碼完成簽名,然後通過表單直傳數據到OSS。

说明 本樣本無法實現分區上傳與斷點續傳。

背景

採用JavaScript客戶端直接簽名(參見JavaScript客戶端簽名直傳)有一個嚴重的安全隱患:OSS AccessKey暴露在前端頁面,這是非常不安全的做法。因此,OSS提供了服務端簽名後直傳的方案。

Demo

您可以通過樣例體驗服務端簽名後直傳效果:PC瀏覽器測試樣例

原理介紹

服務端簽名後直傳的邏輯圖如下:

流程如下:

  1. 用戶發送上傳Policy請求到應用伺服器。
  2. 應用伺服器返回上傳Policy和簽名給用戶。
  3. 用戶使用Plupload直接上傳數據到OSS。

步驟 1:下載並安裝Plugload

Plupload是一款簡單易用且功能強大的檔案上傳工具, 支援多種上傳方式,包括html5、flash、silverlight,、html4。它會智能檢測當前環境,選擇最適合的上傳方式,並且會優先採用Html5方式。請參見Plupload官網進行下載和安裝。

步驟 2:下載應用伺服器代碼

步驟 3:修改設定檔

本樣本採用PHP編寫。將下載包解壓後,修改以下檔案:
  • php/get.php檔案:
    $id= '<yourAccessKeyId>';
     $key= '<yourAccessKeySecret>';
     $host = 'http://post-test.oss-cn-hangzhou.aliyuncs.com
    • $id:您的AccessKeyId
    • $key:您的AessKeySecret
    • $host:格式為BucketName.Endpoint,例如post-test.oss-cn-hangzhou.aliyuncs.com
      说明 關於Endpoint的介紹,請參見Endpoint(訪問網域名稱)
  • upload.js檔案

    將變數severUrl改成伺服器部署的地址,例如http://abc.com:8080/oss-h5-upload-js-php/get.php

步驟 4:設定CORS

HTML表單直接上傳到OSS會產生跨域請求。為了瀏覽安全,需要為Bucket設定跨域規則(CORS),支援Post方法。

具體操作步驟請參見設定跨域訪問。設定如下圖所示:



说明 在低版本IE瀏覽器,Plupload會以Flash方式執行。您需要設定crossdomain.xml ,設定方法請參見OSS Web直傳—使用Flash上傳

步驟 5:體驗服務端簽名後直傳

  1. 將應用伺服器代碼zip包解壓到Web根目錄下。
  2. 在Web瀏覽器中輸入<Web應用伺服器位址>/oss-h5-upload-js-php/index.html,例如http://abc.com:8080/oss-h5-upload-js-php/index.html
  3. 選擇一個或多個檔案進行上傳。
  4. 上傳成功後,通過控制台查看上傳結果。

核心代碼解析

  • 設定成隨機檔案名

    如果想在上傳時固定設定成隨機檔案名,尾碼保持跟客戶端檔案一致,可以將函數改為:

    function check_object_radio() {
        g_object_name_type = 'random_name';
    }
  • 設定成用戶的檔案名

    如果想在上傳時固定設定成用戶的檔案名,可以將函數改為:

    function check_object_radio() {
        g_object_name_type = 'local_name';
    }
  • 設定上傳目錄

    上傳的目錄由服務端(即PHP)指定, 每個客戶端只能上傳到指定的目錄,實現安全隔離。下面的代碼是將上傳目錄改成abc/,注意目錄必須以正斜線(/)結尾。

    $dir = 'abc/';
  • 設定上傳過濾條件

    您可以利用Plupload的屬性filters設定上傳的過濾條件,如設定只能上傳圖片、上傳檔案的大小、不能有重複上傳等。

    var uploader = new plupload.Uploader({
        ……
        filters: {
            mime_types : [ //只允許上傳圖片和zip檔案
            { title : "Image files", extensions : "jpg,gif,png,bmp" },
            { title : "Zip files", extensions : "zip" }
            ], 
            max_file_size : '400kb', //最大隻能上傳400KB的檔案
            prevent_duplicates : true //不允許選取重複檔案
        },
    • mime_types:限制上傳的檔案尾碼
    • max_file_size:限制上傳的檔案大小
    • prevent_duplicates:限制不能重複上傳
      说明 filters過濾條件不是必須的。如果不想設定過濾條件,只要把該項注釋即可。
  • 獲取上傳後的檔案名

    如果要知道檔案上傳成功後的檔案名,可以用Plupload調用FileUploaded事件獲取,如下所示:

    FileUploaded: function(up, file, info) {
                if (info.status == 200)
                {
                    document.getElementById(file.id).getElementsByTagName('b')[0].innerHTML = 'upload to oss success, object name:' + get_uploaded_object_name(file.name);
                }
                else
                {
                    document.getElementById(file.id).getElementsByTagName('b')[0].innerHTML = info.response;
                }
        }

    可以利用如下函數,得到上傳到OSS的檔案名,其中file.name記錄了上傳本地檔案的名稱。

    get_uploaded_object_name(file.name)
  • 上傳簽名

    JavaScript可以從服務端獲取policyBase64、accessid、signature這三個變數,獲取這三個變數的核心代碼如下:

    phpUrl = './php/get.php'
            xmlhttp.open( "GET", phpUrl, false );
            xmlhttp.send( null );
            var obj = eval ("(" + xmlhttp.responseText+ ")");
            host = obj['host']
            policyBase64 = obj['policy']
            accessid = obj['accessid']
            signature = obj['signature']
            expire = parseInt(obj['expire'])
            key = obj['dir']
    xmlhttp.responseText解析如下:
    说明 以下僅為樣本,並不要求必須是相同的格式,但是必須有accessid、policy、signature這三個值。
    {"accessid":"6MKOxxxxxx4AUk44",
    "host":"http://post-test.oss-cn-hangzhou.aliyuncs.com",
    "policy":"eyJleHBpcmF0aW9uIjoiMjAxNS0xMS0wNVQyMDoyMzoyM1oiLCJjxb25kaXRpb25zIjpbWyJjcb250ZW50LWxlbmd0aC1yYW5nZSIsMCwxMDQ4NTc2MDAwXSxbInN0YXJ0cy13aXRoIiwiJGtleSIsInVzZXItZGlyXC8iXV19",
    "signature":"I2u57FWjTKqX/AE6doIdyff151E=",
    "expire":1446726203,"dir":"user-dir/"}
    • accessid:用戶請求的accessid。
    • host:用戶要往哪個網域名稱發送上傳請求。
    • policy:用戶表單上傳的策略(Policy),是經過base64編碼過的字元串。
    • signature:對變數policy簽名後的字元串。
    • expire:上傳策略失效時間,在PolicyText裡指定。在失效時間之前,都可以利用此Policy上傳檔案,所以沒有必要每次上傳都去服務端獲取簽名。
      说明 為了減少服務端的壓力,設計思路是:初始化上傳時,每上傳一個檔案後,獲取一次簽名。然後再上傳時,比較目前時間與簽章時間,看簽章時間是否失效。如果失效了,就重新獲取一次簽名,如果沒有失效,就使用之前的簽名。這裡就用到了變數expire,核心代碼如下:
      now = timestamp = Date.parse(new Date()) / 1000;
      [color=#000000]//可以判斷當前expire是否超過了目前時間,如果超過了目前時間,就重新取一次簽名,緩衝時間為3[/color]
          if (expire < now + 3)
      {  
           ..... 
           phpUrl = './php/get.php'
           xmlhttp.open( "GET", phpUrl, false );
           xmlhttp.send( null );
           ......
      }
      return .

    解析Policy的內容如下:

    {"expiration":"2015-11-05T20:23:23Z",
    "conditions":[["content-length-range",0,1048576000],
    ["starts-with","$key","user-dir/"]]
    说明 Policy的詳細資料請參見Policy基本元素

    上面Policy中增加了starts-with,用來指定此次上傳的檔案名必須以user-dir開頭,用戶可自行指定此字元串。增加starts-with的原因是:在很多場景下,一個應用對應一個Bucket,為了防止數字覆蓋,每個用戶上傳到OSS的檔案都可以有特定的首碼。這樣就存在一個問題,用戶獲取到這個Policy後,在失效期內都能修改上傳首碼,從而上傳到別人的目錄下。為了解決這個問題,可以設定應用伺服器在上傳時就指定用戶上傳的檔案必須是某個首碼。這樣如果用戶獲取到了Policy也沒有辦法上傳到別人的首碼上,從而保證了數據的安全性。

總結

本樣本中,web端向服務端請求籤名,然後直接上傳,不會對服務端產生壓力,而且安全可靠。但是這個樣本有個問題,就是用戶上傳了多少檔案,上傳了什麼檔案,服務端並不能馬上知道,如果想即時了解用戶上傳了什麼檔案,可以採用服務端簽名直傳並設定上傳回調