本文以Ruby語言為例,講解在服務端通過Ruby程式碼完成簽名,並且設定上傳回調,然後通過表單直傳資料到OSS。
前提條件
- 應用伺服器對應的網域名稱可通過公網訪問。
- 確保應用伺服器已經安裝Ruby 2.0以上版本(執行ruby -v命令進行查看)。
- 確保PC端瀏覽器支援JavaScript。
步驟1:配置應用伺服器
- 下載應用伺服器源碼(Ruby版本)。
- 以Ubuntu 16.04為例,將檔案解壓到/home/aliyun/aliyun-oss-appserver-ruby目錄下。
- 進入該目錄,開啟源碼檔案appserver.rb,修改如下程式碼片段:
# 請填寫您的AccessKeyId。 $access_key_id = '<yourAccessKeyId>' # 請填寫您的AccessKeySecret。 $access_key_secret = '<yourAccessKeySecret>' # $host的格式為bucketname.endpoint,請替換為您的真實資訊。 $host = 'https://bucket-name.oss-cn-hangzhou.aliyuncs.com'; # $callbackUrl為上傳回調伺服器的URL,請將下面的IP和Port配置為您自己的真實資訊。 $callback_url = "http://192.0.2.0:8888"; # 使用者上傳檔案時指定的首碼。 $upload_dir = 'user-dir-prefix/'
- $access_key_id:設定您的AccessKeyId。
- $access_key_secret:設定您的AessKeySecret。
- $host:格式為https://bucketname.endpoint,例如https://bucket-name.oss-cn-hangzhou.aliyuncs.com。關於Endpoint的介紹,請參見Endpoint訪問網域名稱。
- $callback_url:設定上傳回調URL,即回調伺服器位址,用於處理應用伺服器與OSS之間的通訊。OSS會在檔案上傳完成後,把檔案上傳資訊通過此回調URL發送給應用伺服器。本例中修改為:
$callback_url="http://192.0.2.0:1234";
。 - $upload_dir:若要設定上傳到OSS檔案的首碼則需要配置此項,否則置空即可。
步驟2:配置用戶端
- 下載用戶端源碼。
- 將檔案解壓,本樣本解壓至D:\aliyun\aliyun-oss-appserver-js目錄。
- 進入該目錄,開啟upload.js檔案,找到下面的代碼語句:
// serverUrl是使用者擷取簽名和Policy等資訊的應用伺服器的URL,請將下面的IP和Port配置為您自己的真實資訊。 serverUrl = 'http://192.0.2.0:8888'
- 將
severUrl
改成應用伺服器的地址,用戶端可以通過它擷取簽名直傳Policy等資訊。如本例中可修改為:serverUrl = 'http://192.0.2.0:1234'
。
步驟3:修改CORS
用戶端進行表單直傳到OSS時,會從瀏覽器向OSS發送帶有Origin
的請求訊息。OSS對帶有Origin
頭的請求訊息會進行跨域規則(CORS)的驗證。因此需要為Bucket設定跨域規則以支援Post方法。
步驟 4:體驗上傳回調
- 啟動應用伺服器。在/home/aliyun/aliyun-oss-appserver-ruby目錄下,執行ruby appserver.rb 192.0.2.0 1234命令啟動應用伺服器。說明 請將IP和連接埠改成您配置的應用伺服器的IP和連接埠。
- 啟動用戶端。在PC端的用戶端源碼目錄中,開啟index.html檔案。重要
index.html檔案不保證相容IE 10以下版本瀏覽器,若使用IE 10以下版本瀏覽器出現問題時,您需要自行調試。
- 上傳檔案。單擊選擇檔案,選擇指定類型的檔案後,單擊開始上傳。上傳成功後,顯示回調伺服器返回的內容。
應用伺服器核心代碼解析
應用伺服器源碼包含了簽名直傳服務和上傳回調服務兩個功能。
- 簽名直傳服務
簽名直傳服務響應用戶端發送給應用伺服器的GET訊息,程式碼片段如下:
def get_token() expire_syncpoint = Time.now.to_i + $expire_time expire = Time.at(expire_syncpoint).utc.iso8601() response.headers['expire'] = expire policy_dict = {} condition_arrary = Array.new array_item = Array.new array_item.push('starts-with') array_item.push('$key') array_item.push($upload_dir) condition_arrary.push(array_item) policy_dict["conditions"] = condition_arrary policy_dict["expiration"] = expire policy = hash_to_jason(policy_dict) policy_encode = Base64.strict_encode64(policy).chomp; h = OpenSSL::HMAC.digest('sha1', $access_key_secret, policy_encode) hs = Digest::MD5.hexdigest(h) sign_result = Base64.strict_encode64(h).strip() callback_dict = {} callback_dict['callbackBodyType'] = 'application/x-www-form-urlencoded'; callback_dict['callbackBody'] = 'filename=${object}&size=${size}&mimeType=${mimeType}&height=${imageInfo.height}&width=${imageInfo.width}'; callback_dict['callbackUrl'] = $callback_url; callback_param = hash_to_jason(callback_dict) base64_callback_body = Base64.strict_encode64(callback_param); token_dict = {} token_dict['accessid'] = $access_key_id token_dict['host'] = $host token_dict['policy'] = policy_encode token_dict['signature'] = sign_result token_dict['expire'] = expire_syncpoint token_dict['dir'] = $upload_dir token_dict['callback'] = base64_callback_body response.headers["Access-Control-Allow-Methods"] = "POST" response.headers["Access-Control-Allow-Origin"] = "*" result = hash_to_jason(token_dict) result end get '/*' do puts "********************* GET " get_token() end
- 上傳回調服務
上傳回調服務響應OSS發送給應用伺服器的POST訊息,程式碼片段如下:
post '/*' do puts "********************* POST" pub_key_url = Base64.decode64(get_header('x-oss-pub-key-url')) pub_key = get_public_key(pub_key_url) rsa = OpenSSL::PKey::RSA.new(pub_key) authorization = Base64.decode64(get_header('authorization')) req_body = request.body.read if request.query_string.empty? then auth_str = CGI.unescape(request.path) + "\n" + req_body else auth_str = CGI.unescape(request.path) + '?' + request.query_string + "\n" + req_body end valid = rsa.public_key.verify( OpenSSL::Digest::MD5.new, authorization, auth_str) if valid #body({'Status' => 'OK'}.to_json) body(hash_to_jason({'Status' => 'OK'})) else halt 400, "Authorization failed!" end end
更多詳情請參見API文檔Callback。