默認情況下,OSS Bucket中的文件是私有的,僅文件擁有者可訪問。本文介紹如何使用OSS Go SDK生成帶有過期時間的GET方法簽名URL,以允許他人臨時下載文件。在有效期內可多次訪問,超期后需重新生成。
注意事項
本文示例代碼以華東1(杭州)的地域ID
cn-hangzhou
為例,默認使用外網Endpoint,如果您希望通過與OSS同地域的其他阿里云產品訪問OSS,請使用內網Endpoint。關于OSS支持的Region與Endpoint的對應關系,請參見OSS地域和訪問域名。本文以從環境變量讀取訪問憑證為例。如何配置訪問憑證,請參見配置訪問憑證。
生成GET方法的簽名URL時,您必須具有
oss:GetObject
權限。具體操作,請參見為RAM用戶授權自定義的權限策略。說明在生成簽名 URL 的過程中,SDK 利用本地存儲的密鑰信息,根據特定算法計算出簽名(signature),然后將其附加到 URL 上,以確保 URL 的有效性和安全性。這一系列計算和構造 URL 的操作都是在客戶端完成的,不涉及網絡請求到服務端。因此,生成簽名 URL 時不需要授予調用者特定權限。然而,為避免第三方用戶無法對簽名URL授權的資源執行相關操作,需要確保調用生成簽名 URL 接口的身份主體已被授予相應的權限。
本文示例代碼使用V4簽名URL,有效期最大為7天。更多信息,請參見簽名版本4(推薦)。
使用過程
使用簽名URL下載文件的過程如下:
方法定義
您可以使用預簽名接口生成預簽名URL,授予對存儲空間中對象的限時訪問權限。在過期時間之前,您可以多次使用預簽名URL。
預簽名接口定義如下:
func (c *Client) Presign(ctx context.Context, request any, optFns ...func(*PresignOptions)) (result *PresignResult, err error)
請求參數列表
參數名 | 類型 | 說明 |
ctx | context.Context | 請求的上下文 |
request | *GetObjectRequest | 設置需要生成簽名URL的接口名 |
optFns | ...func(*PresignOptions) | (可選)設置過期時間,如果不指定,默認有效期為15分鐘 |
其中,PresignOptions選項列舉如下:
選項值 | 類型 | 說明 |
Expires | time.Duration | 從當前時間開始,多長時間過期。例如設置一個有效期為30分鐘,30 * time.Minute |
Expiration | time.Time | 絕對過期時間 |
在簽名版本V4下,有效期最長為7天。同時設置Expiration和Expires時,優先取Expiration。
返回值列表
返回值名 | 類型 | 說明 |
result | *PresignResult | 返回結果,包含預簽名URL,HTTP方法,過期時間和參與簽名的請求頭 |
err | error | 請求的狀態,當請求失敗時,err不為nil |
其中,PresignResult返回值列舉如下:
參數名 | 類型 | 說明 |
Method | string | HTTP方法,和接口對應,例如GetObject接口,返回GET |
URL | string | 預簽名URL |
Expiration | time.Time | 簽名URL的過期時間 |
SignedHeaders | map[string]string | 被簽名的請求頭,例如PutObject接口,設置了Content-Type時,會返回 Content-Type的信息 |
示例代碼
文件擁有者生成GET方法的簽名URL。
package main import ( "context" "flag" "log" "time" "github.com/aliyun/alibabacloud-oss-go-sdk-v2/oss" "github.com/aliyun/alibabacloud-oss-go-sdk-v2/oss/credentials" ) // 定義全局變量 var ( region string // 存儲區域 bucketName string // 存儲空間名稱 objectName string // 對象名稱 ) // init函數用于初始化命令行參數 func init() { flag.StringVar(®ion, "region", "", "The region in which the bucket is located.") flag.StringVar(&bucketName, "bucket", "", "The name of the bucket.") flag.StringVar(&objectName, "object", "", "The name of the object.") } func main() { // 解析命令行參數 flag.Parse() // 檢查bucket名稱是否為空 if len(bucketName) == 0 { flag.PrintDefaults() log.Fatalf("invalid parameters, bucket name required") } // 檢查region是否為空 if len(region) == 0 { flag.PrintDefaults() log.Fatalf("invalid parameters, region required") } // 檢查object名稱是否為空 if len(objectName) == 0 { flag.PrintDefaults() log.Fatalf("invalid parameters, object name required") } // 加載默認配置并設置憑證提供者和區域 cfg := oss.LoadDefaultConfig(). WithCredentialsProvider(credentials.NewEnvironmentVariableCredentialsProvider()). WithRegion(region) // 創建OSS客戶端 client := oss.NewClient(cfg) // 生成GetObject的預簽名URL result, err := client.Presign(context.TODO(), &oss.GetObjectRequest{ Bucket: oss.Ptr(bucketName), Key: oss.Ptr(objectName), //RequestPayer: oss.Ptr("requester"), // 指定請求者身份 }, oss.PresignExpires(10*time.Minute), ) if err != nil { log.Fatalf("failed to get object presign %v", err) } log.Printf("request method:%v\n", result.Method) log.Printf("request expiration:%v\n", result.Expiration) log.Printf("request url:%v\n", result.URL) if len(result.SignedHeaders) > 0 { //當返回結果包含簽名頭時,使用簽名URL發送GET請求時也包含相應的請求頭,以免出現不一致,導致請求失敗和簽名錯誤 log.Printf("signed headers:\n") for k, v := range result.SignedHeaders { log.Printf("%v: %v\n", k, v) } } }
其他人使用GET方法的簽名URL下載文件。
curl
curl -SO "https://examplebucket.oss-cn-hangzhou.aliyuncs.com/exampleobject.txt?x-oss-date=20241112T092756Z&x-oss-expires=3599&x-oss-signature-version=OSS4-HMAC-SHA256&x-oss-credential=LTAI5************/20241112/cn-hangzhou/oss/aliyun_v4_request&x-oss-signature=ed5a939feb8d79a389572719f7e2939939936d0**********"
Java
import java.io.BufferedInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.net.HttpURLConnection; import java.net.URL; public class Demo { public static void main(String[] args) { // 替換為生成的GET方法的簽名URL。 String fileURL = "https://examplebucket.oss-cn-hangzhou.aliyuncs.com/exampleobject.txt?x-oss-date=20241112T092756Z&x-oss-expires=3599&x-oss-signature-version=OSS4-HMAC-SHA256&x-oss-credential=LTAI5************/20241112/cn-hangzhou/oss/aliyun_v4_request&x-oss-signature=ed5a939feb8d79a389572719f7e2939939936d0**********"; // 填寫文件保存的目標路徑,包括文件名和擴展名。 String savePath = "C:/downloads/myfile.txt"; try { downloadFile(fileURL, savePath); System.out.println("Download completed!"); } catch (IOException e) { System.err.println("Error during download: " + e.getMessage()); } } private static void downloadFile(String fileURL, String savePath) throws IOException { URL url = new URL(fileURL); HttpURLConnection httpConn = (HttpURLConnection) url.openConnection(); httpConn.setRequestMethod("GET"); // 檢查響應代碼 int responseCode = httpConn.getResponseCode(); if (responseCode == HttpURLConnection.HTTP_OK) { // 輸入流 InputStream inputStream = new BufferedInputStream(httpConn.getInputStream()); // 輸出流 FileOutputStream outputStream = new FileOutputStream(savePath); byte[] buffer = new byte[4096]; // 緩沖區 int bytesRead; while ((bytesRead = inputStream.read(buffer)) != -1) { outputStream.write(buffer, 0, bytesRead); } outputStream.close(); inputStream.close(); } else { System.out.println("No file to download. Server replied HTTP code: " + responseCode); } httpConn.disconnect(); } }
Node.js
const https = require('https'); const fs = require('fs'); const fileURL = "https://examplebucket.oss-cn-hangzhou.aliyuncs.com/exampleobject.txt?x-oss-date=20241112T092756Z&x-oss-expires=3599&x-oss-signature-version=OSS4-HMAC-SHA256&x-oss-credential=LTAI5************/20241112/cn-hangzhou/oss/aliyun_v4_request&x-oss-signature=ed5a939feb8d79a389572719f7e2939939936d0**********"; const savePath = "C:/downloads/myfile.txt"; https.get(fileURL, (response) => { if (response.statusCode === 200) { const fileStream = fs.createWriteStream(savePath); response.pipe(fileStream); fileStream.on('finish', () => { fileStream.close(); console.log("Download completed!"); }); } else { console.error(`Download failed. Server responded with code: ${response.statusCode}`); } }).on('error', (err) => { console.error("Error during download:", err.message); });
Python
import requests file_url = "https://examplebucket.oss-cn-hangzhou.aliyuncs.com/exampleobject.txt?x-oss-date=20241112T092756Z&x-oss-expires=3599&x-oss-signature-version=OSS4-HMAC-SHA256&x-oss-credential=LTAI5************/20241112/cn-hangzhou/oss/aliyun_v4_request&x-oss-signature=ed5a939feb8d79a389572719f7e2939939936d0**********" save_path = "C:/downloads/myfile.txt" try: response = requests.get(file_url, stream=True) if response.status_code == 200: with open(save_path, 'wb') as f: for chunk in response.iter_content(4096): f.write(chunk) print("Download completed!") else: print(f"No file to download. Server replied HTTP code: {response.status_code}") except Exception as e: print("Error during download:", e)
Go
package main import ( "io" "net/http" "os" ) func main() { fileURL := "https://examplebucket.oss-cn-hangzhou.aliyuncs.com/exampleobject.txt?x-oss-date=20241112T092756Z&x-oss-expires=3599&x-oss-signature-version=OSS4-HMAC-SHA256&x-oss-credential=LTAI5************/20241112/cn-hangzhou/oss/aliyun_v4_request&x-oss-signature=ed5a939feb8d79a389572719f7e2939939936d0**********" savePath := "C:/downloads/myfile.txt" response, err := http.Get(fileURL) if err != nil { panic(err) } defer response.Body.Close() if response.StatusCode == http.StatusOK { outFile, err := os.Create(savePath) if err != nil { panic(err) } defer outFile.Close() _, err = io.Copy(outFile, response.Body) if err != nil { panic(err) } println("Download completed!") } else { println("No file to download. Server replied HTTP code:", response.StatusCode) } }
JavaScript
const fileURL = "https://examplebucket.oss-cn-hangzhou.aliyuncs.com/exampleobject.txt?x-oss-date=20241112T092756Z&x-oss-expires=3599&x-oss-signature-version=OSS4-HMAC-SHA256&x-oss-credential=LTAI5************/20241112/cn-hangzhou/oss/aliyun_v4_request&x-oss-signature=ed5a939feb8d79a389572719f7e2939939936d0**********"; const savePath = "C:/downloads/myfile.txt"; // 文件將在下載時使用的文件名 fetch(fileURL) .then(response => { if (!response.ok) { throw new Error(`Server replied HTTP code: ${response.status}`); } return response.blob(); // 將響應轉換為 blob }) .then(blob => { const link = document.createElement('a'); link.href = window.URL.createObjectURL(blob); link.download = savePath; // 設置下載文件的名字 document.body.appendChild(link); // 此步驟確保鏈接存在于文檔中 link.click(); // 模擬點擊下載鏈接 link.remove(); // 完成后移除鏈接 console.log("Download completed!"); }) .catch(error => { console.error("Error during download:", error); });
Android-Java
import android.os.AsyncTask; import android.os.Environment; import java.io.BufferedInputStream; import java.io.FileOutputStream; import java.io.InputStream; import java.net.HttpURLConnection; import java.net.URL; public class DownloadTask extends AsyncTask<String, String, String> { @Override protected String doInBackground(String... params) { String fileURL = params[0]; String savePath = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS) + "/myfile.txt"; // 修改后的保存路徑 try { URL url = new URL(fileURL); HttpURLConnection httpConn = (HttpURLConnection) url.openConnection(); httpConn.setRequestMethod("GET"); int responseCode = httpConn.getResponseCode(); if (responseCode == HttpURLConnection.HTTP_OK) { InputStream inputStream = new BufferedInputStream(httpConn.getInputStream()); FileOutputStream outputStream = new FileOutputStream(savePath); byte[] buffer = new byte[4096]; int bytesRead; while ((bytesRead = inputStream.read(buffer)) != -1) { outputStream.write(buffer, 0, bytesRead); } outputStream.close(); inputStream.close(); return "Download completed!"; } else { return "No file to download. Server replied HTTP code: " + responseCode; } } catch (Exception e) { return "Error during download: " + e.getMessage(); } } }
Objective-C
#import <Foundation/Foundation.h> int main(int argc, const char * argv[]) { @autoreleasepool { // 定義文件 URL 和保存路徑(修改為有效的路徑) NSString *fileURL = @"https://examplebucket.oss-cn-hangzhou.aliyuncs.com/exampleobject.txt?x-oss-date=20241112T092756Z&x-oss-expires=3599&x-oss-signature-version=OSS4-HMAC-SHA256&x-oss-credential=LTAI5************/20241112/cn-hangzhou/oss/aliyun_v4_request&x-oss-signature=ed5a939feb8d79a389572719f7e2939939936d0**********"; NSString *savePath = @"/Users/your_username/Desktop/myfile.txt"; // 請替換為您的用戶名 // 創建 URL 對象 NSURL *url = [NSURL URLWithString:fileURL]; // 創建下載任務 NSURLSessionDataTask *task = [[NSURLSession sharedSession] dataTaskWithURL:url completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) { // 錯誤處理 if (error) { NSLog(@"Error during download: %@", error.localizedDescription); return; } // 檢查數據 if (!data) { NSLog(@"No data received."); return; } // 保存文件 NSError *writeError = nil; BOOL success = [data writeToURL:[NSURL fileURLWithPath:savePath] options:NSDataWritingAtomic error:&writeError]; if (success) { NSLog(@"Download completed!"); } else { NSLog(@"Error saving file: %@", writeError.localizedDescription); } }]; // 啟動任務 [task resume]; // 讓主線程繼續運行以便異步請求能夠完成 [[NSRunLoop currentRunLoop] run]; } return 0; }
常見使用場景
生成指定版本的文件的GET方法的簽名URL
以下代碼示例在生成GET方法的簽名URL時,指定了文件的版本,以允許他人下載指定版本的文件。
package main
import (
"context"
"flag"
"log"
"time"
"github.com/aliyun/alibabacloud-oss-go-sdk-v2/oss"
"github.com/aliyun/alibabacloud-oss-go-sdk-v2/oss/credentials"
)
// 定義全局變量
var (
region string // 存儲區域
bucketName string // 存儲空間名稱
objectName string // 對象名稱
)
// init函數用于初始化命令行參數
func init() {
flag.StringVar(®ion, "region", "", "The region in which the bucket is located.")
flag.StringVar(&bucketName, "bucket", "", "The name of the bucket.")
flag.StringVar(&objectName, "object", "", "The name of the object.")
}
func main() {
// 解析命令行參數
flag.Parse()
// 檢查bucket名稱是否為空
if len(bucketName) == 0 {
flag.PrintDefaults()
log.Fatalf("invalid parameters, bucket name required")
}
// 檢查region是否為空
if len(region) == 0 {
flag.PrintDefaults()
log.Fatalf("invalid parameters, region required")
}
// 檢查object名稱是否為空
if len(objectName) == 0 {
flag.PrintDefaults()
log.Fatalf("invalid parameters, object name required")
}
// 加載默認配置并設置憑證提供者和區域
cfg := oss.LoadDefaultConfig().
WithCredentialsProvider(credentials.NewEnvironmentVariableCredentialsProvider()).
WithRegion(region)
// 創建OSS客戶端
client := oss.NewClient(cfg)
// 生成GetObject的預簽名URL
result, err := client.Presign(context.TODO(), &oss.GetObjectRequest{
Bucket: oss.Ptr(bucketName),
Key: oss.Ptr(objectName),
VersionId: oss.Ptr("yourVersionId"), // 指定版本ID
},
oss.PresignExpires(10*time.Minute),
)
if err != nil {
log.Fatalf("failed to get object presign %v", err)
}
log.Printf("get object presign result: %#v\n", result)
log.Printf("get object url: %#v\n", result.URL)
}