預設情況下,OSS Bucket中的檔案是私人的,僅檔案擁有者擁有上傳許可權。您可以使用OSS Java SDK產生簽名URL,以允許他人通過該URL上傳檔案。在產生簽名URL時,可以自訂其到期時間以限制訪問持續時間長度。在簽名URL有效期間內,該URL可被多次訪問。如果多次執行上傳操作,會有檔案覆蓋的風險。超出有效期間後,將無法進行上傳,此時需要重建簽名URL。
注意事項
本文以華東1(杭州)外網Endpoint為例。如果您希望通過與OSS同地區的其他阿里雲產品訪問OSS,請使用內網Endpoint。關於OSS支援的Region與Endpoint的對應關係,請參見OSS地區和訪問網域名稱。
本文以從環境變數讀取存取憑證為例。如何配置訪問憑證,請參見Java配置訪問憑證。
本文以OSS網域名稱建立OSSClient為例。如果您希望通過自訂網域名、STS等方式建立OSSClient,請參見建立OSSClient。
產生用於上傳的簽名URL時,您必須具有
oss:PutObject
許可權。具體操作,請參見為RAM使用者授權自訂的權限原則。說明產生簽名URL過程中,SDK利用本機存放區的密鑰資訊,根據特定演算法計算出簽名(signature),然後將其附加到URL上,以確保URL的有效性和安全性。這一系列計算和構造URL的操作都是在用戶端完成,不涉及網路請求到服務端。因此,產生簽名URL時不需要授予調用者特定許可權。但是,為避免第三方使用者無法對簽名URL授權的資源執行相關操作,需要確保調用產生簽名URL介面的身份主體被授予對應的許可權。
本文以V4簽名URL為例,有效期間最大為7天。更多資訊,請參見簽名版本4(推薦)。
使用過程
使用PUT方式的簽名URL上傳檔案的過程如下:
程式碼範例
檔案擁有者產生PUT方法的簽名URL。
import com.aliyun.oss.*; import com.aliyun.oss.common.auth.*; import com.aliyun.oss.common.comm.SignVersion; import com.aliyun.oss.model.GeneratePresignedUrlRequest; import java.net.URL; import java.util.*; import java.util.Date; public class GetSignUrl { public static void main(String[] args) throws Throwable { // 以華東1(杭州)的外網Endpoint為例,其它Region請按實際情況填寫。 String endpoint = "https://oss-cn-hangzhou.aliyuncs.com"; // 從環境變數中擷取訪問憑證。運行本程式碼範例之前,請確保已設定環境變數OSS_ACCESS_KEY_ID和OSS_ACCESS_KEY_SECRET。 EnvironmentVariableCredentialsProvider credentialsProvider = CredentialsProviderFactory.newEnvironmentVariableCredentialsProvider(); // 填寫Bucket名稱,例如examplebucket。 String bucketName = "examplebucket"; // 填寫Object完整路徑,例如exampleobject.txt。Object完整路徑中不能包含Bucket名稱。 String objectName = "exampleobject.txt"; // 填寫Bucket所在地區。以華東1(杭州)為例,Region填寫為cn-hangzhou。 String region = "cn-hangzhou"; // 建立OSSClient執行個體。 ClientBuilderConfiguration clientBuilderConfiguration = new ClientBuilderConfiguration(); clientBuilderConfiguration.setSignatureVersion(SignVersion.V4); OSS ossClient = OSSClientBuilder.create() .endpoint(endpoint) .credentialsProvider(credentialsProvider) .clientConfiguration(clientBuilderConfiguration) .region(region) .build(); URL signedUrl = null; try { // 指定產生的簽名URL到期時間,單位為毫秒。本樣本以設定到期時間為1小時為例。 Date expiration = new Date(new Date().getTime() + 3600 * 1000L); // 產生簽名URL。 GeneratePresignedUrlRequest request = new GeneratePresignedUrlRequest(bucketName, objectName, HttpMethod.PUT); // 設定到期時間。 request.setExpiration(expiration); // 通過HTTP PUT請求產生簽名URL。 signedUrl = ossClient.generatePresignedUrl(request); // 列印簽名URL。 System.out.println("signed url for putObject: " + signedUrl); } catch (OSSException oe) { System.out.println("Caught an OSSException, which means your request made it to OSS, " + "but was rejected with an error response for some reason."); System.out.println("Error Message:" + oe.getErrorMessage()); System.out.println("Error Code:" + oe.getErrorCode()); System.out.println("Request ID:" + oe.getRequestId()); System.out.println("Host ID:" + oe.getHostId()); } catch (ClientException ce) { System.out.println("Caught an ClientException, which means the client encountered " + "a serious internal problem while trying to communicate with OSS, " + "such as not being able to access the network."); System.out.println("Error Message:" + ce.getMessage()); } } }
其他人使用PUT方法的簽名URL上傳檔案。
import org.apache.http.HttpEntity; import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpPut; import org.apache.http.entity.FileEntity; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClients; import java.io.*; import java.net.URL; import java.util.*; public class SignUrlUpload { public static void main(String[] args) throws Throwable { CloseableHttpClient httpClient = null; CloseableHttpResponse response = null; // 將<signedUrl>替換為授權URL。 URL signedUrl = new URL("<signedUrl>"); // 填寫本地檔案的完整路徑。如果未指定本地路徑,則預設從樣本程式所屬專案對應本地路徑中上傳檔案。 String pathName = "C:\\Users\\demo.txt"; try { HttpPut put = new HttpPut(signedUrl.toString()); System.out.println(put); HttpEntity entity = new FileEntity(new File(pathName)); put.setEntity(entity); httpClient = HttpClients.createDefault(); response = httpClient.execute(put); System.out.println("返回上傳狀態代碼:"+response.getStatusLine().getStatusCode()); if(response.getStatusLine().getStatusCode() == 200){ System.out.println("使用網路程式庫上傳成功"); } System.out.println(response.toString()); } catch (Exception e){ e.printStackTrace(); } finally { response.close(); httpClient.close(); } } }
curl -X PUT -T /path/to/local/file "https://exampleobject.oss-cn-hangzhou.aliyuncs.com/exampleobject.txt?x-oss-date=20241112T083238Z&x-oss-expires=3599&x-oss-signature-version=OSS4-HMAC-SHA256&x-oss-credential=LTAI5************%2F20241112%2Fcn-hangzhou%2Foss%2Faliyun_v4_request&x-oss-signature=ed5a939feb8d79a389572719f7e2939939936d0**********"
import requests def upload_file(signed_url, file_path): try: # 開啟檔案 with open(file_path, 'rb') as file: # 發送PUT請求上傳檔案 response = requests.put(signed_url, data=file) print(f"返回上傳狀態代碼:{response.status_code}") if response.status_code == 200: print("使用網路程式庫上傳成功") print(response.text) except Exception as e: print(f"發生錯誤:{e}") if __name__ == "__main__": # 將<signedUrl>替換為授權URL。 signed_url = "<signedUrl>" # 填寫本地檔案的完整路徑。如果未指定本地路徑,則預設從樣本程式所屬專案對應本地路徑中上傳檔案。 file_path = "C:\\Users\\demo.txt" upload_file(signed_url, file_path)
const fs = require('fs'); const axios = require('axios'); async function uploadFile(signedUrl, filePath) { try { // 建立讀取流 const fileStream = fs.createReadStream(filePath); // 發送PUT請求上傳檔案 const response = await axios.put(signedUrl, fileStream, { headers: { 'Content-Type': 'application/octet-stream' // 根據實際情況調整Content-Type } }); console.log(`返回上傳狀態代碼:${response.status}`); if (response.status === 200) { console.log('使用網路程式庫上傳成功'); } console.log(response.data); } catch (error) { console.error(`發生錯誤:${error.message}`); } } // 主函數 (async () => { // 將<signedUrl>替換為授權URL。 const signedUrl = '<signedUrl>'; // 填寫本地檔案的完整路徑。如果未指定本地路徑,則預設從樣本程式所屬專案對應本地路徑中上傳檔案。 const filePath = 'C:\\Users\\demo.txt'; await uploadFile(signedUrl, filePath); })();
#include <iostream> #include <fstream> #include <curl/curl.h> void uploadFile(const std::string& signedUrl, const std::string& filePath) { CURL *curl; CURLcode res; curl_global_init(CURL_GLOBAL_DEFAULT); curl = curl_easy_init(); if (curl) { // 設定URL curl_easy_setopt(curl, CURLOPT_URL, signedUrl.c_str()); // 佈建要求方法為PUT curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L); // 開啟檔案 FILE *file = fopen(filePath.c_str(), "rb"); if (!file) { std::cerr << "無法開啟檔案: " << filePath << std::endl; return; } // 擷取檔案大小 fseek(file, 0, SEEK_END); long fileSize = ftell(file); fseek(file, 0, SEEK_SET); // 設定檔案大小 curl_easy_setopt(curl, CURLOPT_INFILESIZE_LARGE, (curl_off_t)fileSize); // 設定輸入檔案控制代碼 curl_easy_setopt(curl, CURLOPT_READDATA, file); // 執行請求 res = curl_easy_perform(curl); if (res != CURLE_OK) { std::cerr << "curl_easy_perform() 失敗: " << curl_easy_strerror(res) << std::endl; } else { long httpCode = 0; curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &httpCode); std::cout << "返回上傳狀態代碼: " << httpCode << std::endl; if (httpCode == 200) { std::cout << "使用網路程式庫上傳成功" << std::endl; } } // 關閉檔案 fclose(file); // 清理 curl_easy_cleanup(curl); } curl_global_cleanup(); } int main() { // 將<signedUrl>替換為授權URL。 std::string signedUrl = "<signedUrl>"; // 填寫本地檔案的完整路徑。如果未指定本地路徑,則預設從樣本程式所屬專案對應本地路徑中上傳檔案。 std::string filePath = "C:\\Users\\demo.txt"; uploadFile(signedUrl, filePath); return 0; }
package main import ( "fmt" "io" "net/http" "os" ) func uploadFile(signedUrl, filePath string) error { // 開啟檔案 file, err := os.Open(filePath) if err != nil { return fmt.Errorf("無法開啟檔案: %w", err) } defer file.Close() // 建立一個新的HTTP用戶端 client := &http.Client{} // 建立一個PUT請求 req, err := http.NewRequest("PUT", signedUrl, file) if err != nil { return fmt.Errorf("建立請求失敗: %w", err) } // 發送請求 resp, err := client.Do(req) if err != nil { return fmt.Errorf("發送請求失敗: %w", err) } defer resp.Body.Close() // 讀取響應 body, err := io.ReadAll(resp.Body) if err != nil { return fmt.Errorf("讀取響應失敗: %w", err) } fmt.Printf("返回上傳狀態代碼: %d\n", resp.StatusCode) if resp.StatusCode == 200 { fmt.Println("使用網路程式庫上傳成功") } fmt.Println(string(body)) return nil } func main() { // 將<signedUrl>替換為授權URL。 signedUrl := "<signedUrl>" // 填寫本地檔案的完整路徑。如果未指定本地路徑,則預設從樣本程式所屬專案對應本地路徑中上傳檔案。 filePath := "C:\\Users\\demo.txt" err := uploadFile(signedUrl, filePath) if err != nil { fmt.Println("發生錯誤:", err) } }
package com.example.signurlupload; import android.os.AsyncTask; import android.util.Log; import java.io.DataOutputStream; import java.io.FileInputStream; import java.io.IOException; import java.net.HttpURLConnection; import java.net.URL; public class SignUrlUploadActivity { private static final String TAG = "SignUrlUploadActivity"; public void uploadFile(String signedUrl, String filePath) { new UploadTask().execute(signedUrl, filePath); } private class UploadTask extends AsyncTask<String, Void, String> { @Override protected String doInBackground(String... params) { String signedUrl = params[0]; String filePath = params[1]; HttpURLConnection connection = null; DataOutputStream dos = null; FileInputStream fis = null; try { URL url = new URL(signedUrl); connection = (HttpURLConnection) url.openConnection(); connection.setRequestMethod("PUT"); connection.setDoOutput(true); connection.setRequestProperty("Content-Type", "application/octet-stream"); fis = new FileInputStream(filePath); dos = new DataOutputStream(connection.getOutputStream()); byte[] buffer = new byte[1024]; int length; while ((length = fis.read(buffer)) != -1) { dos.write(buffer, 0, length); } dos.flush(); dos.close(); fis.close(); int responseCode = connection.getResponseCode(); Log.d(TAG, "返回上傳狀態代碼: " + responseCode); if (responseCode == 200) { Log.d(TAG, "使用網路程式庫上傳成功"); } return "上傳完成,狀態代碼: " + responseCode; } catch (IOException e) { e.printStackTrace(); return "上傳失敗: " + e.getMessage(); } finally { if (connection != null) { connection.disconnect(); } } } @Override protected void onPostExecute(String result) { Log.d(TAG, result); } } public static void main(String[] args) { SignUrlUploadActivity activity = new SignUrlUploadActivity(); // 將<signedUrl>替換為授權URL。 String signedUrl = "<signedUrl>"; // 填寫本地檔案的完整路徑。如果未指定本地路徑,則預設從樣本程式所屬專案對應本地路徑中上傳檔案。 String filePath = "C:\\Users\\demo.txt"; activity.uploadFile(signedUrl, filePath); } }
其他情境
使用簽名URL指定要求標頭和自訂中繼資料上傳檔案
使用簽名URL分區上傳檔案
常見問題
使用臨時簽名進行檔案上傳時,在上傳過程中籤名到期了,上傳中的檔案會失敗嗎?
如果我在產生URL時未佈建要求頭和自訂中繼資料,在使用URL上傳時還需要配置嗎?
相關文檔
關於擷取V4簽名URL的完整範例程式碼,請參見GitHub樣本。
關於使用簽名URL上傳檔案的API介面說明,請參見GeneratePresignedUrlRequest。