Python使用簽名URL上傳
默認(rèn)情況下,OSS Bucket中的文件是私有的,僅文件擁有者擁有訪問權(quán)限。您可以使用OSS Go SDK生成簽名URL,以允許他人通過該URL上傳文件。在生成簽名URL時,可以自定義其過期時間以限制訪問持續(xù)時長。在簽名URL有效期內(nèi),該URL可被多次訪問。超出有效期后,將無法進(jìn)行上傳,此時需要重新生成簽名URL。
注意事項
本文以華東1(杭州)外網(wǎng)Endpoint為例。如果您希望通過與OSS同地域的其他阿里云產(chǎn)品訪問OSS,請使用內(nèi)網(wǎng)Endpoint。關(guān)于OSS支持的Region與Endpoint的對應(yīng)關(guān)系,請參見OSS地域和訪問域名。
本文以從環(huán)境變量讀取訪問憑證為例。如何配置訪問憑證,請參見配置訪問憑證。
本文以OSS域名新建OSSClient為例。如果您希望通過自定義域名、STS等方式新建OSSClient,請參見新建OSSClient。
生成用于上傳或預(yù)覽文件的簽名URL時,您必須具有
oss:PutObject
權(quán)限。具體操作,請參見為RAM用戶授權(quán)自定義的權(quán)限策略。說明生成簽名URL過程中,SDK利用本地存儲的密鑰信息,根據(jù)特定算法計算出簽名(signature),然后將其附加到URL上,以確保URL的有效性和安全性。這一系列計算和構(gòu)造URL的操作都是在客戶端完成,不涉及網(wǎng)絡(luò)請求到服務(wù)端。因此,生成簽名URL時不需要授予調(diào)用者特定權(quán)限。但是,為避免第三方用戶無法對簽名URL授權(quán)的資源執(zhí)行相關(guān)操作,需要確保調(diào)用生成簽名URL接口的身份主體被授予對應(yīng)的權(quán)限。
本文以V4簽名URL為例,有效期最大為7天。更多信息,請參見簽名版本4(推薦)。
使用過程
使用PUT方式的簽名URL上傳文件的過程如下:
使用簽名URL簡單上傳文件
文件擁有者生成PUT方法的簽名URL。
# -*- coding: utf-8 -*- import oss2 from oss2.credentials import EnvironmentVariableCredentialsProvider # 從環(huán)境變量中獲取訪問憑證。運行本代碼示例之前,請確保已設(shè)置環(huán)境變量OSS_ACCESS_KEY_ID和OSS_ACCESS_KEY_SECRET。 auth = oss2.ProviderAuthV4(EnvironmentVariableCredentialsProvider()) # 填寫Bucket所在地域?qū)?yīng)的Endpoint。以華東1(杭州)為例,Endpoint填寫為https://oss-cn-hangzhou.aliyuncs.com。 endpoint = "https://oss-cn-hangzhou.aliyuncs.com" # 填寫Endpoint對應(yīng)的Region信息,例如cn-hangzhou。注意,v4簽名下,必須填寫該參數(shù) region = "cn-hangzhou" # yourBucketName填寫存儲空間名稱。 bucket = oss2.Bucket(auth, endpoint, "yourBucketName", region=region) # 填寫Object完整路徑,例如exampledir/exampleobject.txt。Object完整路徑中不能包含Bucket名稱。 object_name = 'exampledir/exampleobject.txt' # 生成上傳文件的簽名URL,有效時間為60秒。 # 生成簽名URL時,OSS默認(rèn)會對Object完整路徑中的正斜線(/)進(jìn)行轉(zhuǎn)義,從而導(dǎo)致生成的簽名URL無法直接使用。 # 設(shè)置slash_safe為True,OSS不會對Object完整路徑中的正斜線(/)進(jìn)行轉(zhuǎn)義,此時生成的簽名URL可以直接使用。 url = bucket.sign_url('PUT', object_name, 60, slash_safe=True) print('簽名URL的地址為:', url)
使用獲取的URL簡單上傳文件:
Java 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>替換為授權(quán)URL。 URL signedUrl = new URL("<signedUrl>"); // 填寫本地文件的完整路徑。如果未指定本地路徑,則默認(rèn)從示例程序所屬項目對應(yīng)本地路徑中上傳文件。 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("返回上傳狀態(tài)碼:"+response.getStatusLine().getStatusCode()); if(response.getStatusLine().getStatusCode() == 200){ System.out.println("使用網(wǎng)絡(luò)庫上傳成功"); } System.out.println(response.toString()); } catch (Exception e){ e.printStackTrace(); } finally { response.close(); httpClient.close(); } } }
curl 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**********"
Python import requests def upload_file(signed_url, file_path): try: # 打開文件 with open(file_path, 'rb') as file: # 發(fā)送PUT請求上傳文件 response = requests.put(signed_url, data=file) print(f"返回上傳狀態(tài)碼:{response.status_code}") if response.status_code == 200: print("使用網(wǎng)絡(luò)庫上傳成功") print(response.text) except Exception as e: print(f"發(fā)生錯誤:{e}") if __name__ == "__main__": # 將<signedUrl>替換為授權(quán)URL。 signed_url = "<signedUrl>" # 填寫本地文件的完整路徑。如果未指定本地路徑,則默認(rèn)從示例程序所屬項目對應(yīng)本地路徑中上傳文件。 file_path = "C:\\Users\\demo.txt" upload_file(signed_url, file_path)
JavaScript const fs = require('fs'); const axios = require('axios'); async function uploadFile(signedUrl, filePath) { try { // 創(chuàng)建讀取流 const fileStream = fs.createReadStream(filePath); // 發(fā)送PUT請求上傳文件 const response = await axios.put(signedUrl, fileStream, { headers: { 'Content-Type': 'application/octet-stream' // 根據(jù)實際情況調(diào)整Content-Type } }); console.log(`返回上傳狀態(tài)碼:${response.status}`); if (response.status === 200) { console.log('使用網(wǎng)絡(luò)庫上傳成功'); } console.log(response.data); } catch (error) { console.error(`發(fā)生錯誤:${error.message}`); } } // 主函數(shù) (async () => { // 將<signedUrl>替換為授權(quán)URL。 const signedUrl = '<signedUrl>'; // 填寫本地文件的完整路徑。如果未指定本地路徑,則默認(rèn)從示例程序所屬項目對應(yīng)本地路徑中上傳文件。 const filePath = 'C:\\Users\\demo.txt'; await uploadFile(signedUrl, filePath); })();
C++ #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) { // 設(shè)置URL curl_easy_setopt(curl, CURLOPT_URL, signedUrl.c_str()); // 設(shè)置請求方法為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); // 設(shè)置文件大小 curl_easy_setopt(curl, CURLOPT_INFILESIZE_LARGE, (curl_off_t)fileSize); // 設(shè)置輸入文件句柄 curl_easy_setopt(curl, CURLOPT_READDATA, file); // 執(zhí)行請求 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 << "返回上傳狀態(tài)碼: " << httpCode << std::endl; if (httpCode == 200) { std::cout << "使用網(wǎng)絡(luò)庫上傳成功" << std::endl; } } // 關(guān)閉文件 fclose(file); // 清理 curl_easy_cleanup(curl); } curl_global_cleanup(); } int main() { // 將<signedUrl>替換為授權(quán)URL。 std::string signedUrl = "<signedUrl>"; // 填寫本地文件的完整路徑。如果未指定本地路徑,則默認(rèn)從示例程序所屬項目對應(yīng)本地路徑中上傳文件。 std::string filePath = "C:\\Users\\demo.txt"; uploadFile(signedUrl, filePath); return 0; }
Go 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() // 創(chuàng)建一個新的HTTP客戶端 client := &http.Client{} // 創(chuàng)建一個PUT請求 req, err := http.NewRequest("PUT", signedUrl, file) if err != nil { return fmt.Errorf("創(chuàng)建請求失敗: %w", err) } // 發(fā)送請求 resp, err := client.Do(req) if err != nil { return fmt.Errorf("發(fā)送請求失敗: %w", err) } defer resp.Body.Close() // 讀取響應(yīng) body, err := io.ReadAll(resp.Body) if err != nil { return fmt.Errorf("讀取響應(yīng)失敗: %w", err) } fmt.Printf("返回上傳狀態(tài)碼: %d\n", resp.StatusCode) if resp.StatusCode == 200 { fmt.Println("使用網(wǎng)絡(luò)庫上傳成功") } fmt.Println(string(body)) return nil } func main() { // 將<signedUrl>替換為授權(quán)URL。 signedUrl := "<signedUrl>" // 填寫本地文件的完整路徑。如果未指定本地路徑,則默認(rèn)從示例程序所屬項目對應(yīng)本地路徑中上傳文件。 filePath := "C:\\Users\\demo.txt" err := uploadFile(signedUrl, filePath) if err != nil { fmt.Println("發(fā)生錯誤:", err) } }
Android-Java 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, "返回上傳狀態(tài)碼: " + responseCode); if (responseCode == 200) { Log.d(TAG, "使用網(wǎng)絡(luò)庫上傳成功"); } return "上傳完成,狀態(tài)碼: " + 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>替換為授權(quán)URL。 String signedUrl = "<signedUrl>"; // 填寫本地文件的完整路徑。如果未指定本地路徑,則默認(rèn)從示例程序所屬項目對應(yīng)本地路徑中上傳文件。 String filePath = "C:\\Users\\demo.txt"; activity.uploadFile(signedUrl, filePath); } }
常見使用場景
使用簽名URL上傳指定請求頭和自定義元數(shù)據(jù)的文件
獲取簡單上傳的簽名URL:
# -*- coding: utf-8 -*- import oss2 from oss2.credentials import EnvironmentVariableCredentialsProvider # 從環(huán)境變量中獲取訪問憑證。運行本代碼示例之前,請確保已設(shè)置環(huán)境變量OSS_ACCESS_KEY_ID和OSS_ACCESS_KEY_SECRET。 auth = oss2.ProviderAuthV4(EnvironmentVariableCredentialsProvider()) # 填寫Bucket所在地域?qū)?yīng)的Endpoint。以華東1(杭州)為例,Endpoint填寫為https://oss-cn-hangzhou.aliyuncs.com。 endpoint = "https://oss-cn-hangzhou.aliyuncs.com" # 填寫Endpoint對應(yīng)的Region信息,例如cn-hangzhou。注意,v4簽名下,必須填寫該參數(shù) region = "cn-hangzhou" # yourBucketName填寫存儲空間名稱。 bucket = oss2.Bucket(auth, endpoint, "yourBucketName", region=region) # 填寫Object完整路徑,例如exampledir/exampleobject.txt。Object完整路徑中不能包含Bucket名稱。 object_name = 'exampledir/exampleobject.txt' # 指定Header。 headers = dict() # 指定Content-Type。 headers['Content-Type'] = 'text/plain' # 指定存儲類型。 headers["x-oss-storage-class"] = "Standard" # 指定元數(shù)據(jù) metadata = {'key1': 'value1', 'key2': 'value2'} # 更新headers,添加元數(shù)據(jù)前綴 for key, value in metadata.items(): headers[f'x-oss-meta-{key}'] = value # 生成上傳文件的簽名URL,有效時間為60秒。 # 生成簽名URL時,OSS默認(rèn)會對Object完整路徑中的正斜線(/)進(jìn)行轉(zhuǎn)義,從而導(dǎo)致生成的簽名URL無法直接使用。 # 設(shè)置slash_safe為True,OSS不會對Object完整路徑中的正斜線(/)進(jìn)行轉(zhuǎn)義,此時生成的簽名URL可以直接使用。 url = bucket.sign_url('PUT', object_name, 60, slash_safe=True, headers=headers) print('簽名URL的地址為:', url)
使用獲取的URL簡單上傳文件:
Java 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>替換為授權(quán)URL。 URL signedUrl = new URL("<signedUrl>"); // 填寫本地文件的完整路徑。如果未指定本地路徑,則默認(rèn)從示例程序所屬項目對應(yīng)本地路徑中上傳文件。 String pathName = "C:\\Users\\demo.txt"; // 設(shè)置請求頭,這里的請求頭信息需要與生成URL時的信息一致。 Map<String, String> headers = new HashMap<String, String>(); /*// 指定Object的存儲類型。 headers.put(OSSHeaders.STORAGE_CLASS, StorageClass.Standard.toString()); // 指定ContentType。 headers.put(OSSHeaders.CONTENT_TYPE, "text/txt");*/ // 設(shè)置用戶自定義元數(shù)據(jù),這里的用戶自定義元數(shù)據(jù)需要與生成URL時的信息一致。 Map<String, String> userMetadata = new HashMap<String, String>(); /*userMetadata.put("key1","value1"); userMetadata.put("key2","value2");*/ try { HttpPut put = new HttpPut(signedUrl.toString()); System.out.println(put); HttpEntity entity = new FileEntity(new File(pathName)); put.setEntity(entity); // 如果生成簽名URL時設(shè)置了header參數(shù),例如用戶元數(shù)據(jù),存儲類型等,則調(diào)用簽名URL上傳文件時,也需要將這些參數(shù)發(fā)送至服務(wù)端。如果簽名和發(fā)送至服務(wù)端的不一致,會報簽名錯誤。 for(Map.Entry header: headers.entrySet()){ put.addHeader(header.getKey().toString(),header.getValue().toString()); } for(Map.Entry meta: userMetadata.entrySet()){ // 如果使用userMeta,sdk內(nèi)部會為userMeta拼接"x-oss-meta-"前綴。當(dāng)您使用其他方式生成簽名URL進(jìn)行上傳時,userMeta也需要拼接"x-oss-meta-"前綴。 put.addHeader("x-oss-meta-"+meta.getKey().toString(), meta.getValue().toString()); } httpClient = HttpClients.createDefault(); response = httpClient.execute(put); System.out.println("返回上傳狀態(tài)碼:"+response.getStatusLine().getStatusCode()); if(response.getStatusLine().getStatusCode() == 200){ System.out.println("使用網(wǎng)絡(luò)庫上傳成功"); } System.out.println(response.toString()); } catch (Exception e){ e.printStackTrace(); } finally { response.close(); httpClient.close(); } } }
Python import requests from requests.auth import HTTPBasicAuth import os def upload_file(signed_url, file_path, headers=None, metadata=None): """ 使用預(yù)簽名的URL上傳文件到OSS。 :param signed_url: 預(yù)簽名的URL。 :param file_path: 要上傳的文件的完整路徑。 :param headers: 可選,自定義HTTP頭部。 :param metadata: 可選,自定義元數(shù)據(jù)。 :return: None """ if not headers: headers = {} if not metadata: metadata = {} # 更新headers,添加元數(shù)據(jù)前綴 for key, value in metadata.items(): headers[f'x-oss-meta-{key}'] = value try: with open(file_path, 'rb') as file: response = requests.put(signed_url, data=file, headers=headers) print(f"返回上傳狀態(tài)碼:{response.status_code}") if response.status_code == 200: print("使用網(wǎng)絡(luò)庫上傳成功") else: print("上傳失敗") print(response.text) except Exception as e: print(f"發(fā)生錯誤:{e}") if __name__ == "__main__": # 將<signedUrl>替換為授權(quán)URL。 signed_url = "<signedUrl>" # 填寫本地文件的完整路徑。如果未指定本地路徑,則默認(rèn)從腳本所在目錄中上傳文件。 file_path = "C:\\Users\\demo.txt" # 設(shè)置請求頭,這里的請求頭信息需要與生成URL時的信息一致。 headers = { "Content-Type": "text/txt", "x-oss-storage-class": "Standard" } # 設(shè)置用戶自定義元數(shù)據(jù),這里的用戶自定義元數(shù)據(jù)需要與生成URL時的信息一致。 metadata = { "key1": "value1", "key2": "value2" } upload_file(signed_url, file_path, headers, metadata)
Android-Java import android.os.AsyncTask; import android.util.Log; import java.io.DataOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.net.HttpURLConnection; import java.net.URL; import java.util.HashMap; import java.util.Map; import java.util.Map.Entry; public class SignUrlUploadActivity extends AppCompatActivity { private static final String TAG = "SignUrlUploadActivity"; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // 將<signedUrl>替換為授權(quán)URL。 String signedUrl = "<signedUrl>"; // 填寫本地文件的完整路徑。如果未指定本地路徑,則默認(rèn)從示例程序所屬項目對應(yīng)本地路徑中上傳文件。 String pathName = "/storage/emulated/0/demo.txt"; // 設(shè)置請求頭,這里的請求頭信息需要與生成URL時的信息一致。 Map<String, String> headers = new HashMap<>(); // headers.put("Content-Type", "text/txt"); // headers.put("x-oss-storage-class", "Standard"); // 設(shè)置用戶自定義元數(shù)據(jù),這里的用戶自定義元數(shù)據(jù)需要與生成URL時的信息一致。 Map<String, String> userMetadata = new HashMap<>(); // userMetadata.put("key1", "value1"); // userMetadata.put("key2", "value2"); new UploadTask().execute(signedUrl, pathName, headers, userMetadata); } private class UploadTask extends AsyncTask<Object, Void, Integer> { @Override protected Integer doInBackground(Object... params) { String signedUrl = (String) params[0]; String pathName = (String) params[1]; Map<String, String> headers = (Map<String, String>) params[2]; Map<String, String> userMetadata = (Map<String, String>) params[3]; try { URL url = new URL(signedUrl); HttpURLConnection connection = (HttpURLConnection) url.openConnection(); connection.setRequestMethod("PUT"); connection.setDoOutput(true); connection.setUseCaches(false); // 設(shè)置請求頭 for (Entry<String, String> header : headers.entrySet()) { connection.setRequestProperty(header.getKey(), header.getValue()); } // 設(shè)置用戶自定義元數(shù)據(jù) for (Entry<String, String> meta : userMetadata.entrySet()) { connection.setRequestProperty("x-oss-meta-" + meta.getKey(), meta.getValue()); } // 讀取文件 File file = new File(pathName); FileInputStream fileInputStream = new FileInputStream(file); DataOutputStream dos = new DataOutputStream(connection.getOutputStream()); byte[] buffer = new byte[1024]; int count; while ((count = fileInputStream.read(buffer)) != -1) { dos.write(buffer, 0, count); } fileInputStream.close(); dos.flush(); dos.close(); // 獲取響應(yīng) int responseCode = connection.getResponseCode(); Log.d(TAG, "返回上傳狀態(tài)碼:" + responseCode); if (responseCode == 200) { Log.d(TAG, "使用網(wǎng)絡(luò)庫上傳成功"); } else { Log.d(TAG, "上傳失敗"); } InputStream is = connection.getInputStream(); byte[] responseBuffer = new byte[1024]; StringBuilder responseStringBuilder = new StringBuilder(); while ((count = is.read(responseBuffer)) != -1) { responseStringBuilder.append(new String(responseBuffer, 0, count)); } Log.d(TAG, responseStringBuilder.toString()); return responseCode; } catch (IOException e) { e.printStackTrace(); return -1; } } @Override protected void onPostExecute(Integer result) { super.onPostExecute(result); if (result == 200) { Toast.makeText(SignUrlUploadActivity.this, "上傳成功", Toast.LENGTH_SHORT).show(); } else { Toast.makeText(SignUrlUploadActivity.this, "上傳失敗", Toast.LENGTH_SHORT).show(); } } } }
Go package main import ( "bytes" "fmt" "io/ioutil" "net/http" "os" ) func uploadFile(signedUrl string, filePath string, headers map[string]string, metadata map[string]string) error { // 打開文件 file, err := os.Open(filePath) if err != nil { return err } defer file.Close() // 讀取文件內(nèi)容 fileBytes, err := ioutil.ReadAll(file) if err != nil { return err } // 創(chuàng)建請求 req, err := http.NewRequest("PUT", signedUrl, bytes.NewBuffer(fileBytes)) if err != nil { return err } // 設(shè)置請求頭 for key, value := range headers { req.Header.Set(key, value) } // 設(shè)置用戶自定義元數(shù)據(jù) for key, value := range metadata { req.Header.Set(fmt.Sprintf("x-oss-meta-%s", key), value) } // 發(fā)送請求 client := &http.Client{} resp, err := client.Do(req) if err != nil { return err } defer resp.Body.Close() // 處理響應(yīng) fmt.Printf("返回上傳狀態(tài)碼:%d\n", resp.StatusCode) if resp.StatusCode == 200 { fmt.Println("使用網(wǎng)絡(luò)庫上傳成功") } else { fmt.Println("上傳失敗") } body, _ := ioutil.ReadAll(resp.Body) fmt.Println(string(body)) return nil } func main() { // 將<signedUrl>替換為授權(quán)URL。 signedUrl := "<signedUrl>" // 填寫本地文件的完整路徑。如果未指定本地路徑,則默認(rèn)從示例程序所屬項目對應(yīng)本地路徑中上傳文件。 filePath := "C:\\Users\\demo.txt" // 設(shè)置請求頭,這里的請求頭信息需要與生成URL時的信息一致。 headers := map[string]string{ // "Content-Type": "text/txt", // "x-oss-storage-class": "Standard", } // 設(shè)置用戶自定義元數(shù)據(jù),這里的用戶自定義元數(shù)據(jù)需要與生成URL時的信息一致。 metadata := map[string]string{ // "key1": "value1", // "key2": "value2", } err := uploadFile(signedUrl, filePath, headers, metadata) if err != nil { fmt.Printf("發(fā)生錯誤:%v\n", err) } }
JavaScript const fs = require('fs'); const axios = require('axios'); async function uploadFile(signedUrl, filePath, headers = {}, metadata = {}) { try { // 更新headers,添加元數(shù)據(jù)前綴 for (const [key, value] of Object.entries(metadata)) { headers[`x-oss-meta-${key}`] = value; } // 讀取文件流 const fileStream = fs.createReadStream(filePath); // 發(fā)送PUT請求 const response = await axios.put(signedUrl, fileStream, { headers: headers }); console.log(`返回上傳狀態(tài)碼:${response.status}`); if (response.status === 200) { console.log("使用網(wǎng)絡(luò)庫上傳成功"); } else { console.log("上傳失敗"); } console.log(response.data); } catch (error) { console.error(`發(fā)生錯誤:${error.message}`); } } // 主函數(shù) (async () => { // 將<signedUrl>替換為授權(quán)URL。 const signedUrl = "<signedUrl>"; // 填寫本地文件的完整路徑。如果未指定本地路徑,則默認(rèn)從腳本所在目錄中上傳文件。 const filePath = "C:\\Users\\demo.txt"; // 設(shè)置請求頭,這里的請求頭信息需要與生成URL時的信息一致。 const headers = { // "Content-Type": "text/txt", // "x-oss-storage-class": "Standard" }; // 設(shè)置用戶自定義元數(shù)據(jù),這里的用戶自定義元數(shù)據(jù)需要與生成URL時的信息一致。 const metadata = { // "key1": "value1", // "key2": "value2" }; await uploadFile(signedUrl, filePath, headers, metadata); })();
C++ #include <iostream> #include <fstream> #include <curl/curl.h> #include <map> #include <string> // 回調(diào)函數(shù),用于處理HTTP響應(yīng) size_t WriteCallback(void* contents, size_t size, size_t nmemb, std::string* output) { size_t totalSize = size * nmemb; output->append((char*)contents, totalSize); return totalSize; } void uploadFile(const std::string& signedUrl, const std::string& filePath, const std::map<std::string, std::string>& headers, const std::map<std::string, std::string>& metadata) { CURL* curl; CURLcode res; std::string readBuffer; curl_global_init(CURL_GLOBAL_DEFAULT); curl = curl_easy_init(); if (curl) { // 設(shè)置URL curl_easy_setopt(curl, CURLOPT_URL, signedUrl.c_str()); // 設(shè)置請求方法為PUT curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L); // 打開文件 FILE* file = fopen(filePath.c_str(), "rb"); if (!file) { std::cerr << "無法打開文件: " << filePath << std::endl; return; } // 設(shè)置文件大小 fseek(file, 0, SEEK_END); long fileSize = ftell(file); rewind(file); // 設(shè)置文件讀取回調(diào) curl_easy_setopt(curl, CURLOPT_READDATA, file); curl_easy_setopt(curl, CURLOPT_INFILESIZE_LARGE, (curl_off_t)fileSize); // 設(shè)置請求頭 struct curl_slist* chunk = nullptr; for (const auto& header : headers) { std::string headerStr = header.first + ": " + header.second; chunk = curl_slist_append(chunk, headerStr.c_str()); } for (const auto& meta : metadata) { std::string metaStr = "x-oss-meta-" + meta.first + ": " + meta.second; chunk = curl_slist_append(chunk, metaStr.c_str()); } curl_easy_setopt(curl, CURLOPT_HTTPHEADER, chunk); // 設(shè)置響應(yīng)處理回調(diào) curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback); curl_easy_setopt(curl, CURLOPT_WRITEDATA, &readBuffer); // 執(zhí)行請求 res = curl_easy_perform(curl); // 檢查響應(yīng) if (res != CURLE_OK) { std::cerr << "curl_easy_perform() failed: " << curl_easy_strerror(res) << std::endl; } else { long responseCode; curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &responseCode); std::cout << "返回上傳狀態(tài)碼: " << responseCode << std::endl; if (responseCode == 200) { std::cout << "使用網(wǎng)絡(luò)庫上傳成功" << std::endl; } else { std::cout << "上傳失敗" << std::endl; } std::cout << readBuffer << std::endl; } // 清理 fclose(file); curl_slist_free_all(chunk); curl_easy_cleanup(curl); } curl_global_cleanup(); } int main() { // 將<signedUrl>替換為授權(quán)URL。 std::string signedUrl = "<signedUrl>"; // 填寫本地文件的完整路徑。如果未指定本地路徑,則默認(rèn)從示例程序所屬項目對應(yīng)本地路徑中上傳文件。 std::string filePath = "C:\\Users\\demo.txt"; // 設(shè)置請求頭,這里的請求頭信息需要與生成URL時的信息一致。 std::map<std::string, std::string> headers = { // {"Content-Type", "text/txt"}, // {"x-oss-storage-class", "Standard"} }; // 設(shè)置用戶自定義元數(shù)據(jù),這里的用戶自定義元數(shù)據(jù)需要與生成URL時的信息一致。 std::map<std::string, std::string> metadata = { // {"key1", "value1"}, // {"key2", "value2"} }; uploadFile(signedUrl, filePath, headers, metadata); return 0; }
curl curl -X PUT \ -H "Content-Type: text/txt" \ -H "x-oss-meta-key1: value1" \ -H "x-oss-meta-key2: value2" \ -T "C:\\Users\\demo.txt" \ "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**********"
常見問題
使用臨時簽名進(jìn)行文件上傳時,在上傳過程中簽名過期了,上傳中的文件會失敗嗎?
上傳時不會失敗。
上傳時使用的是預(yù)簽名地址,該url只要是在有效期里(取token的有效期和預(yù)簽名有效期最小值),都可以使用。
如果我在生成URL時未設(shè)置請求頭和自定義元數(shù)據(jù),在使用URL上傳時還需要配置嗎?
不需要配置。
請求頭和自定義元數(shù)據(jù)為非必要參數(shù),如果不設(shè)置請求頭和自定義元數(shù)據(jù)可以將示例中相關(guān)代碼去掉。