默认情况下,OSS存储空间中文件的读写权限是私有,仅文件拥有者可以上传。但文件拥有者可以对指定的文件生成具有临时访问权限的预签名URL,以允许他人使用该预签名URL在有效期内上传文件。该功能适用于授权合作伙伴上传合同,用户上传头像等场景。
注意事项
本文以华东1(杭州)外网Endpoint为例。如果您希望通过与OSS同地域的其他阿里云产品访问OSS,请使用内网Endpoint。关于OSS支持的Region与Endpoint的对应关系,请参见OSS地域和访问域名。
生成PUT方法的预签名URL时,您必须具有
oss:PutObject
权限。具体操作,请参见RAM Policy常见示例。说明生成预签名URL过程中,SDK利用本地存储的密钥信息,根据特定算法计算出签名(signature),然后将其附加到URL上,以确保URL的有效性和安全性。这一系列计算和构造URL的操作都是在客户端完成,不涉及网络请求到服务端。因此,生成预签名URL时不需要授予调用者特定权限。但是,为避免第三方用户无法对预签名URL授权的资源执行相关操作,需要确保调用生成预签名URL接口的身份主体被授予对应的权限。
预签名URL在有效期内可多次访问,但多次执行上传操作,会有文件覆盖的风险。超期后,需执行步骤一重新生成预签名URL以继续访问文件。
预签名URL上传不支持上传FormData格式,若需使用FormData上传数据,建议使用OSS表单上传。
流程概览
使用预签名URL上传文件的过程如下:
详细步骤
步骤一:Bucket拥有者生成PUT方法的预签名URL
通过SDK生成的预签名URL,最大有效时长为7天。若使用STSToken来生成预签名URL,则最大有效时长为43200秒(12小时)。
Java
更多SDK信息,请参见Java使用预签名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());
}
}
}
Go
更多SDK信息,请参见Go使用预签名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)
// 生成PutObject的预签名URL
result, err := client.Presign(context.TODO(), &oss.PutObjectRequest{
Bucket: oss.Ptr(bucketName),
Key: oss.Ptr(objectName),
},
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)
}
}
}
Python
更多SDK信息,请参见Python使用预签名URL上传文件。
# -*- 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)
Node.js
此处仅列举普通场景,如何生成带图片处理参数的预签名URL、如何生成带versionID的预签名URL,请参见Node.js使用预签名URL上传。
const OSS = require("ali-oss");
// 定义一个生成签名 URL 的函数
async function generateSignatureUrl(fileName) {
// 获取预签名URL
const client = await new OSS({
accessKeyId: 'yourAccessKeyId',
accessKeySecret: 'yourAccessKeySecret',
bucket: 'examplebucket',
region: 'oss-cn-hangzhou',
authorizationV4: true
});
return await client.signatureUrlV4('PUT', 3600, {
headers: {} // 请根据实际发送的请求头设置此处的请求头
}, fileName);
}
// 调用函数并传入文件名
generateSignatureUrl('yourFileName').then(url => {
console.log('Generated Signature URL:', url);
}).catch(err => {
console.error('Error generating signature URL:', err);
});
PHP
此处仅列举普通场景,如何生成带versionID的预签名URL,请参见PHP使用预签名URL上传。
<?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\OssClient;
use OSS\Core\OssException;
use OSS\Http\RequestCore;
use OSS\Http\ResponseCore;
use OSS\Credentials\EnvironmentVariableCredentialsProvider;
// 从环境变量中获取访问凭证。运行本代码示例之前,请确保已设置环境变量OSS_ACCESS_KEY_ID和OSS_ACCESS_KEY_SECRET。
$provider = new EnvironmentVariableCredentialsProvider();
// yourEndpoint填写Bucket所在地域对应的Endpoint。以华东1(杭州)为例,Endpoint填写为https://oss-cn-hangzhou.aliyuncs.com。
$endpoint = "yourEndpoint";
// 填写Bucket名称。
$bucket= "examplebucket";
// 填写不包含Bucket名称在内的Object完整路径。
$object = "exampleobject.txt";
// 指定预签名URL的过期时间为600s(最长可达32400s)。
$timeout = 600;
try {
$config = array(
"provider" => $provider,
"endpoint" => $endpoint,
'signatureVersion'=>OssClient::OSS_SIGNATURE_VERSION_V4,
"region"=> "cn-hangzhou"
);
$ossClient = new OssClient($config);
// 生成预签名URL。
$signedUrl = $ossClient->signUrl($bucket, $object, $timeout, "PUT");
print_r($signedUrl);
} catch (OssException $e) {
printf(__FUNCTION__ . ": FAILED\n");
printf($e->getMessage() . "\n");
return;
}
Android
更多SDK信息,请参见Android使用预签名URL上传文件。
// 填写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();
}
C++
更多SDK信息,请参见C++使用预签名URL上传文件。
#include <alibabacloud/oss/OssClient.h>
using namespace AlibabaCloud::OSS;
int main(void)
{
/* 初始化OSS账号信息。*/
/* yourEndpoint填写Bucket所在地域对应的Endpoint。以华东1(杭州)为例,Endpoint填写为https://oss-cn-hangzhou.aliyuncs.com。*/
std::string Endpoint = "yourEndpoint";
/ *yourRegion填写Bucket所在地域对应的Region。以华东1(杭州)为例,Region填写为cn - hangzhou。 * /
std::string Region = "yourRegion";
/* 填写Bucket名称,例如examplebucket。*/
std::string BucketName = "examplebucket";
/* 填写Object完整路径,完整路径中不能包含Bucket名称,例如exampledir/exampleobject.txt。*/
std::string PutobjectUrlName = "exampledir/exampleobject.txt";
/* 初始化网络等资源。*/
InitializeSdk();
ClientConfiguration conf;
conf.signatureVersion = SignatureVersionType::V4;
/* 从环境变量中获取访问凭证。运行本代码示例之前,请确保已设置环境变量OSS_ACCESS_KEY_ID和OSS_ACCESS_KEY_SECRET。*/
auto credentialsProvider = std::make_shared<EnvironmentVariableCredentialsProvider>();
OssClient client(Endpoint, credentialsProvider, conf);
client.SetRegion(Region);
/* 设置签名有效时长,最大有效时间为32400秒。*/
std::time_t t = std::time(nullptr) + 1200;
/* 生成预签名URL。*/
auto genOutcome = client.GeneratePresignedUrl(BucketName, PutobjectUrlName, t, Http::Put);
if (genOutcome.isSuccess()) {
std::cout << "GeneratePresignedUrl success, Gen url:" << genOutcome.result().c_str() << std::endl;
}
else {
/* 异常处理。*/
std::cout << "GeneratePresignedUrl fail" <<
",code:" << genOutcome.error().Code() <<
",message:" << genOutcome.error().Message() <<
",requestId:" << genOutcome.error().RequestId() << std::endl;
return -1;
}
/* 释放网络等资源。*/
ShutdownSdk();
return 0;
}
iOS
更多SDK信息,请参见iOS使用预签名URL上传文件。
// 填写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;
}];
.NET
更多SDK信息,请参见.NET使用预签名URL上传。
using Aliyun.OSS;
using Aliyun.OSS.Common;
// 填写Bucket所在地域对应的Endpoint。以华东1(杭州)为例,Endpoint填写为https://oss-cn-hangzhou.aliyuncs.com。
var endpoint = "https://oss-cn-hangzhou.aliyuncs.com";
// 从环境变量中获取访问凭证。运行本代码示例之前,请确保已设置环境变量OSS_ACCESS_KEY_ID和OSS_ACCESS_KEY_SECRET。
var accessKeyId = Environment.GetEnvironmentVariable("OSS_ACCESS_KEY_ID");
var accessKeySecret = Environment.GetEnvironmentVariable("OSS_ACCESS_KEY_SECRET");
// 填写Bucket名称,例如examplebucket。
var bucketName = "examplebucket";
// 填写Object完整路径,完整路径中不包含Bucket名称,例如exampledir/exampleobject.txt。
var objectName = "exampledir/exampleobject.txt";
var objectContent = "More than just cloud.";
// 创建OSSClient实例。
var client = new OssClient(endpoint, accessKeyId, accessKeySecret);
try
{
// 生成签名URL。
var generatePresignedUriRequest = new GeneratePresignedUriRequest(bucketName, objectName, SignHttpMethod.Put)
{
// 设置签名URL过期时间,默认值为3600秒。
Expiration = DateTime.Now.AddHours(1),
};
var signedUrl = client.GeneratePresignedUri(generatePresignedUriRequest);
}
catch (OssException ex)
{
Console.WriteLine("Failed with error code: {0}; Error info: {1}. \nRequestID:{2}\tHostID:{3}",
ex.ErrorCode, ex.Message, ex.RequestId, ex.HostId);
}
catch (Exception ex)
{
Console.WriteLine("Failed with error info: {0}", ex.Message);
}
C
更多SDK信息,请参见C使用预签名URL下载文件。
#include "oss_api.h"
#include "aos_http_io.h"
/* yourEndpoint填写Bucket所在地域对应的Endpoint。以华东1(杭州)为例,Endpoint填写为https://oss-cn-hangzhou.aliyuncs.com。*/
const char *endpoint = "yourEndpoint";
/* 填写Bucket名称,例如examplebucket。*/
const char *bucket_name = "examplebucket";
/* 填写Object完整路径,完整路径中不能包含Bucket名称,例如exampledir/exampleobject.txt。*/
const char *object_name = "exampledir/exampleobject.txt";
/* 填写本地文件的完整路径。*/
const char *local_filename = "yourLocalFilename";
void init_options(oss_request_options_t *options)
{
options->config = oss_config_create(options->pool);
/* 用char*类型的字符串初始化aos_string_t类型。*/
aos_str_set(&options->config->endpoint, endpoint);
/* 从环境变量中获取访问凭证。运行本代码示例之前,请确保已设置环境变量OSS_ACCESS_KEY_ID和OSS_ACCESS_KEY_SECRET。*/
aos_str_set(&options->config->access_key_id, getenv("OSS_ACCESS_KEY_ID"));
aos_str_set(&options->config->access_key_secret, getenv("OSS_ACCESS_KEY_SECRET"));
/* 是否使用CNAME访问OSS服务。0表示不使用。*/
options->config->is_cname = 0;
/* 设置网络相关参数,例如超时时间等。*/
options->ctl = aos_http_controller_create(options->pool, 0);
}
int main(int argc, char *argv[])
{
/* 在程序入口调用aos_http_io_initialize方法来初始化网络、内存等全局资源。*/
if (aos_http_io_initialize(NULL, 0) != AOSE_OK) {
exit(1);
}
/* 用于内存管理的内存池(pool),等价于apr_pool_t。其实现代码在apr库中。*/
aos_pool_t *pool;
/* 重新创建一个内存池,第二个参数是NULL,表示没有继承其它内存池。*/
aos_pool_create(&pool, NULL);
/* 创建并初始化options,该参数包括endpoint、access_key_id、acces_key_secret、is_cname、curl等全局配置信息。*/
oss_request_options_t *oss_client_options;
/* 在内存池中分配内存给options。*/
oss_client_options = oss_request_options_create(pool);
/* 初始化Client的选项oss_client_options。*/
init_options(oss_client_options);
/* 初始化参数。*/
aos_string_t bucket;
aos_string_t object;
aos_string_t file;
aos_http_request_t *req;
apr_time_t now;
char *url_str;
aos_string_t url;
int64_t expire_time;
int one_hour = 3600;
aos_str_set(&bucket, bucket_name);
aos_str_set(&object, object_name);
aos_str_set(&file, local_filename);
expire_time = now / 1000000 + one_hour;
req = aos_http_request_create(pool);
req->method = HTTP_PUT;
now = apr_time_now();
/* 单位:微秒 */
expire_time = now / 1000000 + one_hour;
/* 生成预签名URL。*/
url_str = oss_gen_signed_url(oss_client_options, &bucket, &object, expire_time, req);
aos_str_set(&url, url_str);
printf("临时上传URL: %s\n", url_str);
/* 释放内存池,相当于释放了请求过程中各资源分配的内存。*/
aos_pool_destroy(pool);
/* 释放之前分配的全局资源。*/
aos_http_io_deinitialize();
return 0;
}
步骤二:其他人使用PUT方法生成的预签名URL上传文件。
使用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**********"
使用代码上传
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();
}
}
}
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)
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)
}
}
Node.js
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);
})();
Browser.js
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>File Upload Example</title>
</head>
<body>
<h1>File Upload Example</h1>
<input type="file" id="fileInput" />
<button id="uploadButton">Upload File</button>
<script>
// 请将此替换为实际的预签名 URL
const signedUrl = "<signedUrl>";
document.getElementById('uploadButton').addEventListener('click', async () => {
const fileInput = document.getElementById('fileInput');
const file = fileInput.files[0];
if (file) {
try {
await upload(file, signedUrl);
} catch (error) {
console.error('Error during upload:', error);
alert('Upload failed: ' + error.message);
}
} else {
console.error('Please select a file');
alert('Please select a file to upload.');
}
});
const upload = async (file, presignedUrl) => {
const chunkSize = 1024 * 1024;
let start = 0;
let end = chunkSize;
while (start < file.size) {
const chunk = file.slice(start, end);
const response = await fetch(presignedUrl, {
method: 'PUT',
body: chunk
});
if (!response.ok) {
throw new Error(`Upload failed for chunk, status: ${response.status}`);
}
console.log('Chunk uploaded successfully');
start = end;
end = start + chunkSize;
}
console.log('File uploaded successfully');
};
</script>
</body>
</html>
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) {
// 设置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;
}
Android
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);
}
}
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分片上传文件
使用预签名URL定义上传策略(携带Header)
了解更多
什么是预签名URL
预签名URL是一种安全链接,通过加密签名和有效期验证,临时授权访问特定的OSS文件。生成预签名URL时,本地基于AK/SK密钥对资源路径、过期时间等参数进行加密计算,生成签名参数并将其添加到URL中,形成完整的预签名URL。其典型格式为:https://BucketName.Endpoint/Object?签名参数
。
当第三方访问该URL时,OSS会校验签名参数,若参数被篡改或过期则拒绝访问。
以下为预签名URL示例。
https://examplebucket.oss-cn-hangzhou.aliyuncs.com/exampleobject.txt?x-oss-process=image%2Fresize%2Cp_10&x-oss-date=20241115T095058Z&x-oss-expires=3600&x-oss-signature-version=OSS4-HMAC-SHA256&x-oss-credential=LTAI***********1Uuu%2F20241115%2Fcn-hangzhou%2Foss%2Faliyun_v4_request&x-oss-signature=6e7*************************1eb79d96
通过这种方式,文件拥有者可以安全地授权第三方访问文件,而无需暴露密钥。
适用场景
短期文件共享:第三方申请上传或下载指定文件,后端便生成带有效期的预签名URL并返回给前端。第三方可以在有效期内使用该URL上传或下载文件,从而保障数据安全。
灵活访问:文件拥有者可以通过邮件或聊天工具等方式分享预签名URL,第三方获取URL后在浏览器地址栏粘贴即可下载文件。