全部產品
Search
文件中心

Object Storage Service:使用檔案URL上傳檔案

更新時間:Dec 13, 2024

簽名URL允許第三方使用者在沒有安全憑證或者授權的情況下進行上傳操作。第三方使用者使用簽名URL上傳檔案後,OSS將在指定的Bucket產生該檔案。

注意事項

  • 產生PUT方法的簽名URL時,您必須具有oss:PutObject許可權。具體操作,請參見RAM Policy常見樣本

    說明

    產生簽名URL過程中,SDK利用本機存放區的密鑰資訊,根據特定演算法計算出簽名(signature),然後將其附加到URL上,以確保URL的有效性和安全性。這一系列計算和構造URL的操作都是在用戶端完成,不涉及網路請求到服務端。因此,產生簽名URL時不需要授予調用者特定許可權。但是,為避免第三方使用者無法對簽名URL授權的資源執行相關操作,需要確保調用產生簽名URL介面的身份主體被授予對應的許可權。

  • 產生私人檔案URL時涉及設定URL的有效時間長度。超出簽名URL設定的有效時間長度後,通過簽名URL上傳檔案時會提示簽名URL已到期,導致無法正常上傳檔案。如果您希望繼續使用簽名URL上傳檔案,需要重新擷取簽名URL。

  • 簽名URL上傳不支援上傳FormData格式,若需使用FormData上傳資料,建議使用OSS表單上傳

操作步驟

  1. 產生簽名URL。

    以下僅列舉常見SDK的產生簽名URL的程式碼範例。關於其他SDK的產生簽名URL程式碼範例,請參見SDK簡介

    Java

    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());
            }
        }
    }       

    PHP

    <?php
    if (is_file(__DIR__ . '/../autoload.php')) {
        require_once __DIR__ . '/../autoload.php';
    }
    if (is_file(__DIR__ . '/../vendor/autoload.php')) {
        require_once __DIR__ . '/../vendor/autoload.php';
    }
    use OSS\Credentials\EnvironmentVariableCredentialsProvider;
    use OSS\OssClient;
    use OSS\Core\OssException;
    use OSS\Http\RequestCore;
    use OSS\Http\ResponseCore;
    
    // 運行本程式碼範例之前,請確保已設定環境變數OSS_ACCESS_KEY_ID和OSS_ACCESS_KEY_SECRET。
    $provider = new EnvironmentVariableCredentialsProvider();
    // yourEndpoint填寫Bucket所在地區對應的Endpoint。以華東1(杭州)為例,Endpoint填寫為https://oss-cn-hangzhou.aliyuncs.com。
    $endpoint = "https://oss-cn-hangzhou.aliyuncs.com";
    // 填寫Bucket名稱。
    $bucket= "examplebucket";
    // 填寫不包含Bucket名稱在內的Object完整路徑。
    $object = "exampleobject.txt";
    // 設定簽名URL的有效時間長度為3600秒。
    $timeout = 3600;
    try {
        $config = array(
            "provider" => $provider,
            "endpoint" => $endpoint,        
        );
        $ossClient = new OssClient($config);
    
        // 產生簽名URL。
        $signedUrl = $ossClient->signUrl($bucket, $object, $timeout, "PUT");
    } catch (OssException $e) {
        printf(__FUNCTION__ . ": FAILED\n");
        printf($e->getMessage() . "\n");
        return;
    }
    print(__FUNCTION__ . ": signedUrl: " . $signedUrl . "\n");

    Node.js

    const OSS = require("ali-oss");
    const { default: axios } = require("axios");
    const fs = require("fs");
    
    const client = new OSS({
      // yourregion填寫Bucket所在地區。以華東1(杭州)為例,Region填寫為oss-cn-hangzhou。
      region: "oss-cn-hangzhou",
      // 從環境變數中擷取訪問憑證。運行本程式碼範例之前,請確保已設定環境變數OSS_ACCESS_KEY_ID和OSS_ACCESS_KEY_SECRET。
      accessKeyId: process.env.OSS_ACCESS_KEY_ID,
      accessKeySecret: process.env.OSS_ACCESS_KEY_SECRET,
      // 填寫Bucket名稱。
      bucket: "examplebucket",
    });
    
    // 組建檔案的簽名URL。
    const url = client.signatureUrl("exampleobject.txt", {
      method: "PUT",
      "Content-Type": "application/x-www-form-urlencoded",
    });
    console.log(url);

    Python

    # -*- coding: utf-8 -*-
    import oss2
    from oss2.credentials import EnvironmentVariableCredentialsProvider
    
    # 從環境變數中擷取訪問憑證。運行本程式碼範例之前,請確保已設定環境變數OSS_ACCESS_KEY_ID和OSS_ACCESS_KEY_SECRET。
    auth = oss2.ProviderAuthV4(EnvironmentVariableCredentialsProvider())
    
    # 填寫Bucket所在地區對應的Endpoint。以華東1(杭州)為例,Endpoint填寫為https://oss-cn-hangzhou.aliyuncs.com。
    endpoint = "https://oss-cn-hangzhou.aliyuncs.com"
    
    # 填寫Endpoint對應的Region資訊,例如cn-hangzhou。注意,v4簽名下,必須填寫該參數
    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預設會對Object完整路徑中的正斜線(/)進行轉義,從而導致產生的簽名URL無法直接使用。
    # 設定slash_safe為True,OSS不會對Object完整路徑中的正斜線(/)進行轉義,此時產生的簽名URL可以直接使用。
    url = bucket.sign_url('PUT', object_name, 60, slash_safe=True)
    print('簽名URL的地址為:', url)     

    Android

    // 填寫Bucket名稱,例如examplebucket。
    String bucketName = "examplebucket";
    // 填寫不包含Bucket名稱在內源Object的完整路徑,例如exampleobject.txt。
    String objectKey = "exampleobject.txt";
    // 設定content-type。
    String contentType = "application/octet-stream";
    String url = null;
    try {
        // 產生用於上傳檔案的簽名URL。
        GeneratePresignedUrlRequest request = new GeneratePresignedUrlRequest(bucketName, objectKey);
        // 設定簽名URL的到期時間為30分鐘。
        request.setExpiration(30*60);
        request.setContentType(contentType);    
        request.setMethod(HttpMethod.PUT);
        url = oss.presignConstrainedObjectURL(request);
        Log.d("url", url);
    } catch (ClientException e) {
        e.printStackTrace();
    }

    Go

    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(&region, "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)
    
    	// 產生PutObject的預簽名URL
    	result, err := client.Presign(context.TODO(), &oss.PutObjectRequest{
    		Bucket: oss.Ptr(bucketName),
    		Key:    oss.Ptr(objectName),
    		//ContentType: oss.Ptr("text/txt"),                                 // 請確保在服務端產生該簽名URL時設定的ContentType與在使用URL時設定的ContentType一致
    		//Metadata:    map[string]string{"key1": "value1", "key2": "value2"}, // 請確保在服務端產生該簽名URL時設定的Metadata與在使用URL時設定的Metadata一致
    	},
    		oss.PresignExpires(10*time.Minute),
    	)
    	if err != nil {
    		log.Fatalf("failed to put 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發送Put請求時,需要設定相應的要求標頭
    		log.Printf("signed headers:\n")
    		for k, v := range result.SignedHeaders {
    			log.Printf("%v: %v\n", k, v)
    		}
    	}
    }

    iOS

    // 填寫Bucket名稱。
    NSString *bucketName = @"examplebucket";
    // 填寫Object名稱。
    NSString *objectKey = @"exampleobject.txt";
    NSURL *file = [NSURL fileURLWithPath:@"<filePath>"];
    NSString *contentType = [OSSUtil detemineMimeTypeForFilePath:file.absoluteString uploadName:objectKey];
    __block NSString *urlString;
    // 產生用於上傳的簽名URL,並指定簽名URL到期時間為30分鐘。
    OSSTask *task = [client presignConstrainURLWithBucketName:bucketName
                                                withObjectKey:objectKey
                                                   httpMethod:@"PUT"
                                       withExpirationInterval:30 * 60
                                               withParameters:@{}
                                                  contentType:contentType
                                                   contentMd5:nil];
    [task continueWithBlock:^id _Nullable(OSSTask * _Nonnull task) {
        if (task.error) {
            NSLog(@"presign error: %@", task.error);
        } else {
            urlString = task.result;
            NSLog(@"url: %@", urlString);
        }
        return nil;
    }];
  2. 使用簽名URL上傳檔案。

    以下僅列舉常見SDK使用簽名URL簡單上傳的程式碼範例。關於其他SDK使用簽名URL簡單上傳的程式碼範例,請參見SDK簡介

    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>替換為授權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();
            }
        }
    }   

    PHP

    <?php
    
    if (is_file(__DIR__ . '/../autoload.php')) {
        require_once __DIR__ . '/../autoload.php';
    }
    if (is_file(__DIR__ . '/../vendor/autoload.php')) {
        require_once __DIR__ . '/../vendor/autoload.php';
    }
    
    use OSS\Http\RequestCore;
    use OSS\Http\ResponseCore;
    
    // 填寫步驟1產生的簽名URL。
    $signedUrl = 'yourSignedUrl';
    $content = "Hello OSS";
    $request = new RequestCore($signedUrl);
    // 使用簽名URL上傳檔案。
    $request->set_method('PUT');
    $request->add_header('Content-Type', '');
    $request->add_header('Content-Length', strlen($content));
    $request->set_body($content);
    $request->send_request();
    $res = new ResponseCore($request->get_response_header(),
        $request->get_response_body(), $request->get_response_code());
    if ($res->isOK()) {
        print(__FUNCTION__ . ": OK" . "\n");
    } else {
        print(__FUNCTION__ . ": FAILED" . "\n");
    };  

    Node.js

    const OSS = require("ali-oss");
    const { default: axios } = require("axios");
    const fs = require("fs");
    
    // 填寫步驟1產生的簽名URL。
    const url = "yourSignedUrl";
    
    // 指定本地檔案的完整路徑。
    const file = fs.readFileSync("D:\\examplefile.txt");
    
    // 使用簽名URL上傳檔案。
    axios({
      url,
      method: "PUT",
      data: file,
    })
      .then((r) => console.log(r))
      .catch((e) => console.log(e));
    

    Python

    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)

    Browser.js

    <!DOCTYPE html>
    <html lang="en">
      <head>
        <meta charset="UTF-8" />
        <title>Document</title>
      </head>
      <body>
        <script src="https://gosspublic.alicdn.com/aliyun-oss-sdk-6.18.0.min.js"></script>
        <script>
          // 填寫步驟1產生的簽名URL。
          const url = "yourSignatureUrl";
    
          var xhr = new XMLHttpRequest();
          xhr.open("PUT", url, true);
    
          xhr.onload = function () {
            // 請求結束後,在此處編寫處理代碼。
          };
    
          // xhr.send(null);
          xhr.send("string");
          // xhr.send(new Blob());
          // xhr.send(new Int8Array());
          // xhr.send({ form: 'data' });
          // xhr.send(document);
        </script>
      </body>
    </html>

    Android

    // 填寫產生的簽名URL。
    String url = "";
    // 指定本地檔案的完整路徑。
    String localFile = "/storage/emulated/0/oss/examplefile";
    // 設定content-type。
    String contentType = "application/octet-stream";
    // 通過簽名URL上傳檔案。
    OkHttpClient client = new OkHttpClient();
    Request putRequest = new Request.Builder()
            .url(url)
            .put(RequestBody.create(MediaType.parse(contentType), new File(localFile)))
            .build();
    client.newCall(putRequest).enqueue(new Callback() {
        @Override
        public void onFailure(Call call, IOException e) {
            e.printStackTrace();
        }
    
        @Override
        public void onResponse(Call call, Response response) throws IOException {
            Log.d("response", response.body().string());
        }
    });

    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()
    
    	// 建立一個新的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)
    	}
    }

    iOS

    // 通過簽名URL上傳檔案。
    NSURL * url = [NSURL URLWithString:urlString];
    NSMutableURLRequest * request = [NSMutableURLRequest requestWithURL:url];
    request.HTTPMethod = @"PUT";
    request.allHTTPHeaderFields = @{OSSHttpHeaderContentType: contentType};
    NSURLSession * session = [NSURLSession sharedSession];
    NSURLSessionTask * sessionTask = [session uploadTaskWithRequest:request
                                                           fromFile:file
                                                  completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
        if (error) {
            NSLog(@"upload error: %@", error);
            return;
        } else if (((NSHTTPURLResponse*)response).statusCode == 203 ||
                   ((NSHTTPURLResponse*)response).statusCode >= 300) {
            NSString *body = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
            NSLog(@"upload error: %@", body);
            return;
        }
        NSLog(@"upload success");
    }];
    [sessionTask resume];

相關文檔

當您希望使用簽名URL以分區上傳的方式上傳大檔案到OSS時,您需要先初始化分區上傳,然後把每一個分區產生一個對應的上傳簽名URL,並返回給第三方應用。第三方應用可以使用這些簽名URL上傳所有的分區資訊,然後合并分區來達到通過簽名URL實現分區上傳的目的。關於產生分區上傳的簽名URL以及使用簽名URL臨時授權分區上傳的程式碼範例,請參見使用簽名URL下載