Unggah langsung dari klien memungkinkan Anda mengunggah file dari klien secara langsung ke Object Storage Service (OSS). Berbeda dengan unggah proxy sisi server, metode ini tidak mengalirkan file melalui server aplikasi Anda, sehingga meningkatkan kecepatan unggah dan menghemat sumber daya server. Topik ini menjelaskan manfaat, implementasi keamanan, serta praktik terbaik untuk unggah langsung dari klien.
Mengapa menggunakan unggah langsung dari klien
Pada arsitektur server-klien yang umum, metode unggah file yang lazim adalah menggunakan proxy sisi server. Klien mengunggah file ke server aplikasi, lalu server aplikasi tersebut mengunggah file ke OSS. Dalam proses ini, data dikirimkan melalui jaringan sebanyak dua kali, sehingga mengonsumsi bandwidth jaringan yang tidak perlu dan meningkatkan beban pada server. Untuk mengatasi masalah ini, Anda dapat menghubungkan klien langsung ke OSS guna mengunggah file, sehingga melewati server aplikasi.
Cara mengimplementasikan unggah langsung dari klien
Untuk mengimplementasikan unggah langsung dari klien, Anda harus menangani dua isu utama berikut:
Akses lintas-origin
Jika klien Anda berupa aplikasi web atau mini program, Anda harus menangani pembatasan akses lintas-origin. Untuk alasan keamanan, browser dan kontainer mini program biasanya membatasi akses lintas-origin. Pembatasan ini mencegah kode sisi klien terhubung langsung ke OSS. Anda dapat mengonfigurasi aturan akses lintas-origin untuk bucket OSS agar memungkinkan aplikasi web atau mini program dari domain tertentu mengakses OSS secara langsung. Untuk informasi selengkapnya, lihat Pengaturan lintas-origin.
Otorisasi aman
Mengunggah file ke OSS memerlukan pasangan AccessKey dari pengguna Resource Access Management (RAM) untuk menyelesaikan otentikasi signature. Namun, menyimpan pasangan AccessKey jangka panjang di klien dapat menyebabkan eksposur dan menimbulkan risiko keamanan. Untuk mengatasi masalah ini, Anda dapat memilih salah satu solusi berikut guna memastikan unggah yang aman:
-
Hasilkan kredensial akses sementara STS di server
Untuk sebagian besar skenario unggah file, kami merekomendasikan penggunaan SDK Security Token Service (STS) di server Anda untuk mendapatkan kredensial akses sementara STS. Klien kemudian dapat menggunakan kredensial sementara ini bersama SDK OSS untuk mengunggah file secara langsung. Klien dapat menggunakan ulang kredensial akses sementara STS yang dihasilkan server untuk membuat signature. Metode ini cocok untuk skenario yang melibatkan unggah multi-bagian file besar dan unggah yang dapat dilanjutkan. Perlu diperhatikan bahwa pemanggilan layanan STS yang terlalu sering dapat memicu pembatasan kecepatan. Oleh karena itu, kami merekomendasikan agar Anda menyimpan cache kredensial sementara STS dan melakukan refresh sebelum masa berlakunya habis. Untuk mencegah penyalahgunaan kredensial akses sementara STS oleh klien, kami merekomendasikan penambahan kebijakan akses guna membatasi lebih lanjut izin yang dimilikinya. Untuk informasi selengkapnya, lihat Apa itu STS.
-
Hasilkan signature dan Post Policy yang diperlukan untuk PostObject di server
Untuk skenario yang memerlukan pembatasan atribut file yang diunggah, Anda dapat menghasilkan signature Post, PostPolicy, dan informasi lain yang diperlukan untuk PostObject di server. Klien kemudian dapat menggunakan informasi ini untuk mengunggah file secara langsung dengan batasan tertentu, tanpa bergantung pada SDK OSS. Anda dapat menggunakan PostPolicy yang dihasilkan server untuk membatasi file yang diunggah klien, misalnya dengan membatasi ukuran atau jenis file. Solusi ini cocok untuk mengunggah file melalui formulir HTML. Perlu diperhatikan bahwa solusi ini tidak mendukung unggah multi-bagian file besar atau unggah yang dapat dilanjutkan. Untuk informasi selengkapnya, lihat PostObject.
-
Hasilkan URL yang ditandatangani yang diperlukan untuk PutObject di server
Untuk skenario unggah file sederhana, Anda dapat menggunakan SDK OSS di server untuk menghasilkan URL yang ditandatangani yang diperlukan untuk PutObject. Klien kemudian dapat menggunakan URL yang ditandatangani tersebut untuk mengunggah file secara langsung tanpa bergantung pada SDK OSS. Perlu diperhatikan bahwa solusi ini tidak cocok untuk unggah multi-bagian file besar atau unggah yang dapat dilanjutkan. Menghasilkan URL yang ditandatangani untuk setiap bagian di server dan mengembalikannya ke klien akan meningkatkan jumlah interaksi server serta kompleksitas permintaan jaringan. Selain itu, klien mungkin memodifikasi konten atau urutan bagian, yang dapat menghasilkan file gabungan yang salah. Untuk informasi selengkapnya, lihat Signature V1.
Hasilkan kredensial akses sementara STS di server
Proses berikut menjelaskan cara server memberikan otorisasi kepada klien untuk mengunggah file ke OSS menggunakan kredensial akses sementara STS.
-
Klien meminta kredensial akses sementara dari server aplikasi.
-
Server aplikasi menggunakan SDK STS untuk memanggil operasi AssumeRole dan mendapatkan kredensial akses sementara.
-
STS menghasilkan dan mengembalikan kredensial akses sementara ke server aplikasi.
-
Server aplikasi mengembalikan kredensial akses sementara tersebut ke klien.
-
Klien menggunakan SDK OSS dan kredensial akses sementara tersebut untuk mengunggah file ke OSS.
-
OSS mengembalikan respons sukses ke klien.
Kode contoh
Contoh berikut menunjukkan potongan kode inti. Untuk kode lengkap, lihat proyek contoh: sts.zip.
Kode contoh sisi server
Kode contoh berikut menunjukkan cara menghasilkan kredensial akses sementara di server:
Kode ini mendukung penerapan satu klik. Anda dapat menerapkan kode ini ke Function Compute (FC) dengan satu klik.oss-upload-sts-app
Python
import json
from alibabacloud_tea_openapi.models import Config
from alibabacloud_sts20150401.client import Client as Sts20150401Client
from alibabacloud_sts20150401 import models as sts_20150401_models
from alibabacloud_credentials.client import Client as CredentialClient
# Ganti <YOUR_ROLE_ARN> dengan ARN peran RAM yang memiliki izin untuk mengunggah file ke bucket OSS tertentu.
role_arn_for_oss_upload = '<YOUR_ROLE_ARN>'
# Tetapkan <YOUR_REGION_ID> ke wilayah layanan STS, seperti cn-hangzhou.
region_id = '<YOUR_REGION_ID>'
def get_sts_token():
# Saat Anda menginisialisasi CredentialClient tanpa menentukan parameter apa pun, rantai kredensial default digunakan.
# Saat menjalankan program secara lokal, Anda dapat menentukan pasangan AccessKey menggunakan variabel lingkungan ALIBABA_CLOUD_ACCESS_KEY_ID dan ALIBABA_CLOUD_ACCESS_KEY_SECRET.
# Saat menjalankan program pada instance ECS, instance ECI, atau di Container Service, Anda dapat menentukan instance RAM role yang dilampirkan menggunakan variabel lingkungan ALIBABA_CLOUD_ECS_METADATA. SDK secara otomatis mengambil kredensial sementara STS.
config = Config(region_id=region_id, credential=CredentialClient())
sts_client = Sts20150401Client(config=config)
assume_role_request = sts_20150401_models.AssumeRoleRequest(
role_arn=role_arn_for_oss_upload,
# Tetapkan <YOUR_ROLE_SESSION_NAME> ke nama sesi kustom, seperti oss-role-session.
role_session_name='<YOUR_ROLE_SESSION_NAME>'
)
response = sts_client.assume_role(assume_role_request)
token = json.dumps(response.body.credentials.to_map())
return token
Java
import com.aliyun.sts20150401.Client;
import com.aliyun.sts20150401.models.AssumeRoleRequest;
import com.aliyun.sts20150401.models.AssumeRoleResponse;
import com.aliyun.sts20150401.models.AssumeRoleResponseBody;
import com.aliyun.tea.TeaException;
import com.aliyun.teautil.models.RuntimeOptions;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import com.aliyun.teaopenapi.models.Config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import static com.aliyun.teautil.Common.assertAsString;
@RestController
public class StsController {
@Autowired
private Client stsClient;
@GetMapping("/get_sts_token_for_oss_upload")
public AssumeRoleResponseBody.AssumeRoleResponseBodyCredentials generateStsToken() {
AssumeRoleRequest assumeRoleRequest = new AssumeRoleRequest()
.setDurationSeconds(3600L)
// Tetapkan <YOUR_ROLE_SESSION_NAME> ke nama sesi kustom, seperti my-website-server.
.setRoleSessionName("<YOUR_ROLE_SESSION_NAME>")
// Ganti <YOUR_ROLE_ARN> dengan ARN peran RAM yang memiliki izin untuk mengunggah file ke bucket OSS tertentu. Anda dapat memperoleh ARN peran dari detail peran RAM.
.setRoleArn("<YOUR_ROLE_ARN>");
RuntimeOptions runtime = new RuntimeOptions();
try {
AssumeRoleResponse response = stsClient.assumeRoleWithOptions(assumeRoleRequest, runtime);
return response.body.credentials;
} catch (TeaException error) {
// Cetak error jika diperlukan.
assertAsString(error.message);
return null;
} catch (Exception error) {
assertAsString(error.getMessage());
return null;
}
}
}
@Configuration
public class StsClientConfiguration {
@Bean
public Client stsClient() {
// Saat Anda menginisialisasi klien kredensial tanpa meneruskan parameter apa pun, alat Credentials menggunakan rantai kredensial default untuk menginisialisasi klien.
Config config = new Config();
config.endpoint = "sts.cn-hangzhou.aliyuncs.com";
try {
com.aliyun.credentials.Client credentials = new com.aliyun.credentials.Client();
config.setCredential(credentials);
return new Client(config);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
}
Go
package main
import (
"encoding/json"
"net/http"
"os"
openapi "github.com/alibabacloud-go/darabonba-openapi/v2/client"
sts20150401 "github.com/alibabacloud-go/sts-20150401/v2/client"
util "github.com/alibabacloud-go/tea-utils/v2/service"
"github.com/alibabacloud-go/tea/tea"
)
/**
* Inisialisasi klien dengan pasangan AccessKey.
* @param accessKeyId
* @param accessKeySecret
* @return Client
* @throws Exception
*/
func CreateClient(accessKeyId *string, accessKeySecret *string) (*sts20150401.Client, error) {
config := &openapi.Config{
// Wajib. ID AccessKey Anda.
AccessKeyId: accessKeyId,
// Wajib. Rahasia AccessKey Anda.
AccessKeySecret: accessKeySecret,
}
// Untuk informasi selengkapnya tentang endpoint, lihat https://api.aliyun.com/product/Sts.
config.Endpoint = tea.String("sts.cn-hangzhou.aliyuncs.com")
return sts20150401.NewClient(config)
}
func AssumeRole(client *sts20150401.Client) (*sts20150401.AssumeRoleResponse, error) {
assumeRoleRequest := &sts20150401.AssumeRoleRequest{
DurationSeconds: tea.Int64(3600),
RoleArn: tea.String("acs:ram::1379186349531844:role/admin-oss"),
RoleSessionName: tea.String("peiyu-demo"),
}
return client.AssumeRoleWithOptions(assumeRoleRequest, &util.RuntimeOptions{})
}
func handler(w http.ResponseWriter, r *http.Request) {
if r.URL.Path == "/" {
http.ServeFile(w, r, "templates/index.html")
return
} else if r.URL.Path == "/get_sts_token_for_oss_upload" {
client, err := CreateClient(tea.String(os.Getenv("ALIBABA_CLOUD_ACCESS_KEY_ID")), tea.String(os.Getenv("ALIBABA_CLOUD_ACCESS_KEY_SECRET")))
if err != nil {
panic(err)
}
assumeRoleResponse, err := AssumeRole(client)
if err != nil {
panic(err)
}
responseBytes, err := json.Marshal(assumeRoleResponse)
if err != nil {
panic(err)
}
w.Header().Set("Content-Type", "application/json")
w.Write(responseBytes)
return
}
http.NotFound(w, r)
}
func main() {
http.HandleFunc("/", handler)
http.ListenAndServe(":8080", nil)
}
PHP
<?php
require_once 'vendor/autoload.php';
use AlibabaCloud\Client\AlibabaCloud;
use AlibabaCloud\Client\Exception\ClientException;
use AlibabaCloud\Client\Exception\ServerException;
use AlibabaCloud\Sts\Sts;
// Inisialisasi klien Alibaba Cloud.
AlibabaCloud::accessKeyClient(getenv('ALIBABA_CLOUD_ACCESS_KEY_ID'), getenv('ALIBABA_CLOUD_ACCESS_KEY_SECRET'))
->regionId('cn-hangzhou')
->asDefaultClient();
// Buat permintaan STS.
$request = Sts::v20150401()->assumeRole();
// Kirim permintaan STS dan dapatkan hasilnya.
// Tetapkan <YOUR_ROLE_SESSION_NAME> ke nama sesi kustom, seperti oss-role-session.
// Ganti <YOUR_ROLE_ARN> dengan ARN peran RAM yang memiliki izin untuk mengunggah file ke bucket OSS tertentu.
$result = $request
->withRoleSessionName("<YOUR_ROLE_SESSION_NAME>")
->withDurationSeconds(3600)
->withRoleArn("<YOUR_ROLE_ARN>")
->request();
// Dapatkan informasi kredensial dari hasil permintaan STS.
$credentials = $result->get('Credentials');
// Bangun data JSON yang akan dikembalikan.
$response = [
'AccessKeyId' => $credentials['AccessKeyId'],
'AccessKeySecret' => $credentials['AccessKeySecret'],
'SecurityToken' => $credentials['SecurityToken'],
];
// Tetapkan header respons ke application/json.
header('Content-Type: application/json');
// Konversi hasil ke format JSON dan cetak.
echo json_encode(['Credentials' => $response]);
?>
Node.js
const express = require("express");
const { STS } = require('ali-oss');
const app = express();
const path = require("path");
app.use(express.static(path.join(__dirname, "templates")));
// Konfigurasikan variabel lingkungan ALIBABA_CLOUD_ACCESS_KEY_ID.
const accessKeyId = process.env.ALIBABA_CLOUD_ACCESS_KEY_ID;
// Konfigurasikan variabel lingkungan ALIBABA_CLOUD_ACCESS_KEY_SECRET.
const accessKeySecret = process.env.ALIBABA_CLOUD_ACCESS_KEY_SECRET;
app.get('/get_sts_token_for_oss_upload', (req, res) => {
let sts = new STS({
accessKeyId: accessKeyId,
accessKeySecret: accessKeySecret
});
// Tetapkan roleArn ke ARN peran yang diperoleh pada Langkah 2, misalnya acs:ram::175708322470****:role/ramtest.
// Tetapkan policy ke kebijakan akses kustom untuk membatasi lebih lanjut izin kredensial akses sementara STS. Jika Anda tidak menentukan kebijakan, kredensial akses sementara STS yang dikembalikan secara default memiliki semua izin dari peran yang ditentukan.
// 3000 adalah waktu kedaluwarsa dalam detik.
// Gunakan sessionName untuk menentukan nama sesi peran kustom guna membedakan token yang berbeda, misalnya sessiontest.
sts.assumeRole('<YOUR_ROLE_ARN>', ``, '3000', 'sessiontest').then((result) => {
console.log(result);
res.json({
AccessKeyId: result.credentials.AccessKeyId,
AccessKeySecret: result.credentials.AccessKeySecret,
SecurityToken: result.credentials.SecurityToken,
});
}).catch((err) => {
console.log(err);
res.status(400).json(err.message);
});
});
app.listen(8000, () => {
console.log("http://127.0.0.1:8000");
});
Ruby
require 'sinatra'
require 'base64'
require 'open-uri'
require 'cgi'
require 'openssl'
require 'json'
require 'sinatra/reloader'
require 'sinatra/content_for'
require 'aliyunsdkcore'
# Tetapkan jalur folder publik ke folder templates di direktori saat ini.
set :public_folder, File.dirname(__FILE__) + '/templates'
def get_sts_token_for_oss_upload()
client = RPCClient.new(
# Konfigurasikan variabel lingkungan ALIBABA_CLOUD_ACCESS_KEY_ID.
access_key_id: ENV['ALIBABA_CLOUD_ACCESS_KEY_ID'],
# Konfigurasikan variabel lingkungan ALIBABA_CLOUD_ACCESS_KEY_SECRET.
access_key_secret: ENV['ALIBABA_CLOUD_ACCESS_KEY_SECRET'],
endpoint: 'https://sts.cn-hangzhou.aliyuncs.com',
api_version: '2015-04-01'
)
response = client.request(
action: 'AssumeRole',
params: {
# Tetapkan RoleArn ke ARN peran yang diperoleh pada Langkah 2, misalnya acs:ram::175708322470****:role/ramtest.
"RoleArn": "acs:ram::175708322470****:role/ramtest",
# 3600 adalah waktu kedaluwarsa dalam detik.
"DurationSeconds": 3600,
# Gunakan RoleSessionName untuk menentukan nama sesi peran kustom guna membedakan token yang berbeda, misalnya sessiontest.
"RoleSessionName": "sessiontest"
},
opts: {
method: 'POST',
format_params: true
}
)
end
if ARGV.length == 1
$server_port = ARGV[0]
elsif ARGV.length == 2
$server_ip = ARGV[0]
$server_port = ARGV[1]
end
$server_ip = "0.0.0.0"
$server_port = 8000
puts "App server is running on: http://#{$server_ip}:#{$server_port}"
set :bind, $server_ip
set :port, $server_port
get '/get_sts_token_for_oss_upload' do
token = get_sts_token_for_oss_upload()
response = {
"AccessKeyId" => token["Credentials"]["AccessKeyId"],
"AccessKeySecret" => token["Credentials"]["AccessKeySecret"],
"SecurityToken" => token["Credentials"]["SecurityToken"]
}
response.to_json
end
get '/*' do
puts "********************* GET "
send_file File.join(settings.public_folder, 'index.html')
end
C#
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Aliyun.OSS;
using System;
using System.IO;
using AlibabaCloud.SDK.Sts20150401;
using System.Text.Json;
namespace YourNamespace
{
public class Program
{
private ILogger<Program> _logger;
public static AlibabaCloud.SDK.Sts20150401.Client CreateClient(string accessKeyId, string accessKeySecret)
{
var config = new AlibabaCloud.OpenApiClient.Models.Config
{
AccessKeyId = accessKeyId,
AccessKeySecret = accessKeySecret,
Endpoint = "sts.cn-hangzhou.aliyuncs.com"
};
return new AlibabaCloud.SDK.Sts20150401.Client(config);
}
public static void Main(string[] args)
{
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
builder.Logging.AddConsole();
var serviceProvider = builder.Services.BuildServiceProvider();
var logger = serviceProvider.GetRequiredService<ILogger<Program>>();
app.UseStaticFiles();
app.MapGet("/", async (context) =>
{
var filePath = Path.Combine(Directory.GetCurrentDirectory(), "templates/index.html");
var htmlContent = await File.ReadAllTextAsync(filePath);
await context.Response.WriteAsync(htmlContent);
logger.LogInformation("GET request to root path");
});
app.MapGet("/get_sts_token_for_oss_upload", async (context) =>
{
var program = new Program(logger);
var client = CreateClient(Environment.GetEnvironmentVariable("ALIBABA_CLOUD_ACCESS_KEY_ID"), Environment.GetEnvironmentVariable("ALIBABA_CLOUD_ACCESS_KEY_SECRET"));
var assumeRoleRequest = new AlibabaCloud.SDK.Sts20150401.Models.AssumeRoleRequest();
// Tetapkan <YOUR_ROLE_SESSION_NAME> ke nama sesi kustom, seperti oss-role-session.
assumeRoleRequest.RoleSessionName = "<YOUR_ROLE_SESSION_NAME>";
// Ganti <YOUR_ROLE_ARN> dengan ARN peran RAM yang memiliki izin untuk mengunggah file ke bucket OSS tertentu.
assumeRoleRequest.RoleArn = "<YOUR_ROLE_ARN>";
assumeRoleRequest.DurationSeconds = 3600;
var runtime = new AlibabaCloud.TeaUtil.Models.RuntimeOptions();
var response = client.AssumeRoleWithOptions(assumeRoleRequest, runtime);
var credentials = response.Body.Credentials;
var jsonResponse = JsonSerializer.Serialize(new
{
AccessKeyId = credentials.AccessKeyId,
AccessKeySecret = credentials.AccessKeySecret,
Expiration = credentials.Expiration,
SecurityToken = credentials.SecurityToken
});
context.Response.ContentType = "application/json";
await context.Response.WriteAsync(jsonResponse);
});
app.Run();
}
public Program(ILogger<Program> logger)
{
_logger = logger;
}
}
}
Kode contoh sisi klien
Kode contoh berikut menunjukkan cara klien web menggunakan kredensial akses sementara untuk mengunggah file ke OSS:
let credentials = null;
const form = document.querySelector("form");
form.addEventListener("submit", async (event) => {
event.preventDefault();
// Untuk mengurangi panggilan ke layanan STS, ambil kredensial lagi hanya ketika yang saat ini telah kedaluwarsa.
if (isCredentialsExpired(credentials)) {
const response = await fetch("/get_sts_token_for_oss_upload", {
method: "GET",
});
if (!response.ok) {
// Tangani kode status HTTP error.
throw new Error(
`Gagal mendapatkan token STS: ${response.status} ${response.statusText}`
);
}
credentials = await response.json();
}
const client = new OSS({
// Tetapkan <YOUR_BUCKET> ke nama bucket OSS Anda.
bucket: "<YOUR_BUCKET>",
// Tetapkan <YOUR_REGION> ke wilayah tempat bucket OSS berada, misalnya region: 'oss-cn-hangzhou'.
region: "oss-<YOUR_REGION>",
accessKeyId: credentials.AccessKeyId,
accessKeySecret: credentials.AccessKeySecret,
stsToken: credentials.SecurityToken,
});
const fileInput = document.querySelector("#file");
const file = fileInput.files[0];
const result = await client.put(file.name, file);
console.log(result);
});
/**
* Periksa apakah kredensial sementara telah kedaluwarsa.
**/
function isCredentialsExpired(credentials) {
if (!credentials) {
return true;
}
const expireDate = new Date(credentials.Expiration);
const now = new Date();
// Jika periode validitas kurang dari satu menit, kredensial dianggap telah kedaluwarsa.
return expireDate.getTime() - now.getTime() <= 60000;
}Hasilkan signature dan Post Policy yang diperlukan untuk PostObject di server
Proses berikut menjelaskan cara server memberikan otorisasi kepada klien untuk mengunggah file ke OSS menggunakan signature Post dan Post Policy.
-
Klien meminta signature Post, Post Policy, dan informasi lain dari server aplikasi.
-
Server aplikasi menghasilkan dan mengembalikan signature Post, Post Policy, dan informasi lain tersebut ke klien.
-
Klien menggunakan signature Post, Post Policy, dan informasi lain tersebut untuk memanggil operasi PostObject dan mengunggah file ke OSS melalui formulir HTML.
-
OSS mengembalikan respons sukses ke klien.
Kode contoh
Contoh berikut menunjukkan potongan kode inti. Untuk kode lengkap, lihat proyek contoh: postsignature.zip.
Kode contoh sisi server
Kode contoh berikut menunjukkan cara menghasilkan signature Post, Post Policy, dan informasi lain di server:
Kode ini mendukung penerapan satu klik. Anda dapat menerapkan kode ini ke Function Compute (FC) dengan satu klik.oss-upload-post-signature-app
Python
import os
from hashlib import sha1 as sha
import json
import base64
import hmac
import datetime
import time
# Konfigurasikan variabel lingkungan OSS_ACCESS_KEY_ID.
access_key_id = os.environ.get('OSS_ACCESS_KEY_ID')
# Konfigurasikan variabel lingkungan OSS_ACCESS_KEY_SECRET.
access_key_secret = os.environ.get('OSS_ACCESS_KEY_SECRET')
# Ganti <YOUR_BUCKET> dengan nama bucket Anda.
bucket = '<YOUR_BUCKET>'
# Format host adalah bucketname.endpoint. Ganti <YOUR_BUCKET> dengan nama bucket Anda. Ganti <YOUR_ENDPOINT> dengan endpoint OSS, seperti oss-cn-hangzhou.aliyuncs.com.
host = 'https://<YOUR_BUCKET>.<YOUR_ENDPOINT>'
# Tentukan awalan untuk file yang diunggah ke OSS.
upload_dir = 'user-dir-prefix/'
# Tentukan waktu kedaluwarsa dalam detik.
expire_time = 3600
def generate_expiration(seconds):
"""
Hasilkan waktu kedaluwarsa dengan menentukan periode validitas dalam detik.
:param seconds: Periode validitas dalam detik.
:return: String waktu ISO 8601, seperti "2014-12-01T12:00:00.000Z".
"""
now = int(time.time())
expiration_time = now + seconds
gmt = datetime.datetime.utcfromtimestamp(expiration_time).isoformat()
gmt += 'Z'
return gmt
def generate_signature(access_key_secret, expiration, conditions, policy_extra_props=None):
"""
Hasilkan string signature.
:param access_key_secret: Rahasia AccessKey akun yang memiliki izin untuk mengakses bucket tujuan.
:param expiration: Waktu kedaluwarsa signature, dalam format ISO 8601 dan UTC. Contoh: "2014-12-01T12:00:00.000Z".
:param conditions: Kondisi kebijakan yang digunakan untuk membatasi nilai yang diizinkan saat mengunggah formulir.
:param policy_extra_props: Parameter kebijakan tambahan. Jika parameter baru ditambahkan ke kebijakan, Anda dapat meneruskannya sebagai dictionary.
:return: signature, string signature.
"""
policy_dict = {
'expiration': expiration,
'conditions': conditions
}
if policy_extra_props is not None:
policy_dict.update(policy_extra_props)
policy = json.dumps(policy_dict).strip()
policy_encode = base64.b64encode(policy.encode())
h = hmac.new(access_key_secret.encode(), policy_encode, sha)
sign_result = base64.b64encode(h.digest()).strip()
return sign_result.decode()
def generate_upload_params():
policy = {
# Periode validitas.
"expiration": generate_expiration(expire_time),
# Batasan.
"conditions": [
# Kode status yang dikembalikan setelah unggah berhasil jika success_action_redirect tidak ditentukan. Nilai default adalah 204.
["eq", "$success_action_status", "200"],
# Nilai bidang formulir harus dimulai dengan awalan yang ditentukan. Misalnya, untuk menentukan bahwa nilai key harus dimulai dengan user/user1, Anda dapat menulis ["starts-with", "$key", "user/user1"].
["starts-with", "$key", upload_dir],
# Batasi ukuran objek yang diunggah minimum dan maksimum dalam byte.
["content-length-range", 1, 1000000],
# Batasi file yang diunggah hanya ke jenis gambar tertentu.
["in", "$content-type", ["image/jpg", "image/png"]]
]
}
signature = generate_signature(access_key_secret, policy.get('expiration'), policy.get('conditions'))
response = {
'policy': base64.b64encode(json.dumps(policy).encode('utf-8')).decode(),
'ossAccessKeyId': access_key_id,
'signature': signature,
'host': host,
'dir': upload_dir
# Anda dapat menambahkan parameter lain di sini.
}
return json.dumps(response)
Java
package com.aliyun.sample;
import com.aliyun.oss.ClientException;
import com.aliyun.oss.OSS;
import com.aliyun.oss.OSSException;
import com.aliyun.oss.common.utils.BinaryUtil;
import com.aliyun.oss.model.MatchMode;
import com.aliyun.oss.model.PolicyConditions;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.codehaus.jettison.json.JSONObject;
import java.util.Date;
@Controller
public class PostSignatureController {
@Autowired
private OSS ossClient;
@Autowired
private OssConfig ossConfig;
@GetMapping("/get_post_signature_for_oss_upload")
@ResponseBody
public String generatePostSignature() {
JSONObject response = new JSONObject();
try {
long expireEndTime = System.currentTimeMillis() + ossConfig.getExpireTime() * 1000;
Date expiration = new Date(expireEndTime);
PolicyConditions policyConds = new PolicyConditions();
policyConds.addConditionItem(PolicyConditions.COND_CONTENT_LENGTH_RANGE, 0, 1048576000);
policyConds.addConditionItem(MatchMode.StartWith, PolicyConditions.COND_KEY, ossConfig.getDir());
String postPolicy = ossClient.generatePostPolicy(expiration, policyConds);
byte[] binaryData = postPolicy.getBytes("utf-8");
String encodedPolicy = BinaryUtil.toBase64String(binaryData);
String postSignature = ossClient.calculatePostSignature(postPolicy);
response.put("ossAccessKeyId", ossConfig.getAccessKeyId());
response.put("policy", encodedPolicy);
response.put("signature", postSignature);
response.put("dir", ossConfig.getDir());
response.put("host", ossConfig.getHost());
} 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("HTTP Status Code: " + oe.getRawResponseError());
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());
} catch (Exception e) {
System.out.println("Caught an unexpected exception: " + e.getMessage());
}
return response.toString();
}
}
Go
package main
import (
"crypto/hmac"
"crypto/sha1"
"encoding/base64"
"encoding/json"
"fmt"
"io"
"net/http"
"os"
"time"
)
var (
// Konfigurasikan variabel lingkungan OSS_ACCESS_KEY_ID.
accessKeyId = os.Getenv("OSS_ACCESS_KEY_ID")
// Konfigurasikan variabel lingkungan OSS_ACCESS_KEY_SECRET.
accessKeySecret = os.Getenv("OSS_ACCESS_KEY_SECRET")
// Format host adalah bucketname.endpoint. Ganti ${your-bucket} dengan nama bucket Anda. Ganti ${your-endpoint} dengan endpoint OSS, seperti oss-cn-hangzhou.aliyuncs.com.
host = "http://${your-bucket}.${your-endpoint}"
// Tentukan awalan untuk file yang diunggah ke OSS.
uploadDir = "user-dir-prefix/"
// Tentukan waktu kedaluwarsa dalam detik.
expireTime = int64(3600)
)
type ConfigStruct struct {
Expiration string `json:"expiration"`
Conditions [][]interface{} `json:"conditions"`
}
type PolicyToken struct {
AccessKeyId string `json:"ossAccessKeyId"`
Host string `json:"host"`
Signature string `json:"signature"`
Policy string `json:"policy"`
Directory string `json:"dir"`
}
func getGMTISO8601(expireEnd int64) string {
return time.Unix(expireEnd, 0).UTC().Format("2006-01-02T15:04:05Z")
}
func getPolicyToken() string {
now := time.Now().Unix()
expireEnd := now + expireTime
tokenExpire := getGMTISO8601(expireEnd)
var config ConfigStruct
config.Expiration = tokenExpire
// Tambahkan batasan awalan file.
config.Conditions = append(config.Conditions, []interface{}{"starts-with", "$key", uploadDir})
// Tambahkan batasan ukuran file, misalnya dari 1 KB hingga 10 MB.
minSize := int64(1024)
maxSize := int64(10 * 1024 * 1024)
config.Conditions = append(config.Conditions, []interface{}{"content-length-range", minSize, maxSize})
result, err := json.Marshal(config)
if err != nil {
fmt.Println("callback json err:", err)
return ""
}
encodedResult := base64.StdEncoding.EncodeToString(result)
h := hmac.New(sha1.New, []byte(accessKeySecret))
io.WriteString(h, encodedResult)
signedStr := base64.StdEncoding.EncodeToString(h.Sum(nil))
policyToken := PolicyToken{
AccessKeyId: accessKeyId,
Host: host,
Signature: signedStr,
Policy: encodedResult,
Directory: uploadDir,
}
response, err := json.Marshal(policyToken)
if err != nil {
fmt.Println("json err:", err)
return ""
}
return string(response)
}
func handler(w http.ResponseWriter, r *http.Request) {
if r.URL.Path == "/" {
http.ServeFile(w, r, "templates/index.html")
return
} else if r.URL.Path == "/get_post_signature_for_oss_upload" {
policyToken := getPolicyToken()
w.Header().Set("Content-Type", "application/json")
w.Write([]byte(policyToken))
return
}
http.NotFound(w, r)
}
func main() {
http.HandleFunc("/", handler)
http.ListenAndServe(":8080", nil)
}
PHP
<?php
function gmt_iso8601($time)
{
return str_replace('+00:00', '.000Z', gmdate('c', $time));
}
// Dapatkan kredensial akses dari variabel lingkungan. Sebelum menjalankan kode contoh ini, pastikan variabel lingkungan ALIBABA_CLOUD_ACCESS_KEY_ID dan ALIBABA_CLOUD_ACCESS_KEY_SECRET telah dikonfigurasi.
$accessKeyId = getenv("ALIBABA_CLOUD_ACCESS_KEY_ID");
$accessKeySecret = getenv("ALIBABA_CLOUD_ACCESS_KEY_SECRET");
// Format $host adalah '<YOUR-BUCKET>.<YOUR-ENDPOINT>'. Ganti variabel dengan informasi aktual Anda.
$host = 'http://<YOUR-BUCKET>.<YOUR-ENDPOINT>';
// Awalan yang ditentukan pengguna saat mengunggah file.
$dir = 'user-dir-prefix/';
$now = time();
// Tetapkan kebijakan untuk kedaluwarsa dalam 10 detik. Setelah periode ini, kebijakan menjadi tidak valid.
$expire = 30;
$end = $now + $expire;
$expiration = gmt_iso8601($end);
// Ukuran file maksimum. Anda dapat menyetelnya sendiri.
$condition = array(0 => 'content-length-range', 1 => 0, 2 => 1048576000);
$conditions[] = $condition;
// Data yang diunggah harus dimulai dengan $dir, jika tidak unggah akan gagal. Langkah ini tidak wajib tetapi direkomendasikan demi keamanan untuk mencegah pengguna mengunggah ke direktori lain melalui kebijakan.
$start = array(0 => 'starts-with', 1 => '$key', 2 => $dir);
$conditions[] = $start;
$arr = array('expiration' => $expiration, 'conditions' => $conditions);
$policy = json_encode($arr);
$base64_policy = base64_encode($policy);
$string_to_sign = $base64_policy;
$signature = base64_encode(hash_hmac('sha1', $string_to_sign, $accessKeySecret, true));
$response = array();
$response['ossAccessKeyId'] = $accessKeyId;
$response['host'] = $host;
$response['policy'] = $base64_policy;
$response['signature'] = $signature;
$response['dir'] = $dir;
echo json_encode($response);
Node.js
const express = require("express");
const { Buffer } = require("buffer");
const OSS = require("ali-oss");
const app = express();
const path = require("path");
const config = {
// Konfigurasikan variabel lingkungan ALIBABA_CLOUD_ACCESS_KEY_ID.
accessKeyId: process.env.ALIBABA_CLOUD_ACCESS_KEY_ID,
// Konfigurasikan variabel lingkungan ALIBABA_CLOUD_ACCESS_KEY_SECRET.
accessKeySecret: process.env.ALIBABA_CLOUD_ACCESS_KEY_SECRET,
// Ganti <YOUR-BUCKET> dengan nama bucket Anda.
bucket: "<YOUR-BUCKET>",
// Tentukan awalan untuk file yang diunggah ke OSS.
dir: "prefix/",
};
app.use(express.static(path.join(__dirname, "templates")));
app.get("/get_post_signature_for_oss_upload", async (req, res) => {
const client = new OSS(config);
const date = new Date();
// Tetapkan periode validitas signature dalam detik.
date.setSeconds(date.getSeconds() + 3600);
const policy = {
expiration: date.toISOString(),
conditions: [
// Tetapkan batas ukuran untuk file yang diunggah.
["content-length-range", 0, 1048576000],
// Batasi bucket tempat file dapat diunggah.
{ bucket: client.options.bucket },
],
};
const formData = await client.calculatePostSignature(policy);
const host = `http://${config.bucket}.${
(await client.getBucketLocation()).location
}.aliyuncs.com`.toString();
const params = {
policy: formData.policy,
signature: formData.Signature,
ossAccessKeyId: formData.OSSAccessKeyId,
host,
dir: config.dir,
};
res.json(params);
});
app.get(/^(.+)*\.(html|js)$/i, async (req, res) => {
res.sendFile(path.join(__dirname, "./templates", req.originalUrl));
});
app.listen(8000, () => {
console.log("http://127.0.0.1:8000");
});
Ruby
require 'sinatra'
require 'base64'
require 'open-uri'
require 'cgi'
require 'openssl'
require 'json'
require 'sinatra/reloader'
require 'sinatra/content_for'
# Atur jalur folder publik ke folder templat di direktori saat ini.
set :public_folder, File.dirname(__FILE__) + '/templates'
# Konfigurasikan variabel lingkungan ALIBABA_CLOUD_ACCESS_KEY_ID.
$access_key_id = ENV['ALIBABA_CLOUD_ACCESS_ID']
# Konfigurasikan variabel lingkungan ALIBABA_CLOUD_ACCESS_KEY_SECRET.
$access_key_secret = ENV['ALIBABA_CLOUD_ACCESS_SECRET']
# Format $host adalah <bucketname>.<endpoint>. Ganti <bucketname> dan <endpoint> dengan informasi Anda yang sebenarnya.
$host = 'http://<bucketname>.<endpoint>';
# Awalan untuk file yang diunggah pengguna.
$upload_dir = 'user-dir-prefix/'
# Waktu kedaluwarsa dalam detik.
$expire_time = 30
$server_ip = "0.0.0.0"
$server_port = 8000
if ARGV.length == 1
$server_port = ARGV[0]
elsif ARGV.length == 2
$server_ip = ARGV[0]
$server_port = ARGV[1]
end
puts "App server is running on: http://#{$server_ip}:#{$server_port}"
def hash_to_jason(source_hash)
jason_string = source_hash.to_json;
jason_string.gsub!("\":[", "\": [")
jason_string.gsub!("\",\"", "\", \"")
jason_string.gsub!("],\"", "], \"")
jason_string.gsub!("\":\"", "\": \"")
jason_string
end
def get_token()
expire_syncpoint = Time.now.to_i + $expire_time
expire = Time.at(expire_syncpoint).utc.iso8601()
response.headers['expire'] = expire
policy_dict = {}
condition_arrary = Array.new
array_item = Array.new
array_item.push('starts-with')
array_item.push('$key')
array_item.push($upload_dir)
condition_arrary.push(array_item)
policy_dict["conditions"] = condition_arrary
policy_dict["expiration"] = expire
policy = hash_to_jason(policy_dict)
policy_encode = Base64.strict_encode64(policy).chomp;
h = OpenSSL::HMAC.digest('sha1', $access_key_secret, policy_encode)
hs = Digest::MD5.hexdigest(h)
sign_result = Base64.strict_encode64(h).strip()
token_dict = {}
token_dict['ossAccessKeyId'] = $access_key_id
token_dict['host'] = $host
token_dict['policy'] = policy_encode
token_dict['signature'] = sign_result
token_dict['expire'] = expire_syncpoint
token_dict['dir'] = $upload_dir
result = hash_to_jason(token_dict)
result
end
set :bind, $server_ip
set :port, $server_port
get '/get_post_signature_for_oss_upload' do
token = get_token()
puts "Token: #{token}"
token
end
get '/*' do
puts "********************* GET "
send_file File.join(settings.public_folder, 'index.html')
end
end
if ARGV.length == 1
$server_port = ARGV[0]
elsif ARGV.length == 2
$server_ip = ARGV[0]
$server_port = ARGV[1]
end
$server_ip = "0.0.0.0"
$server_port = 8000
puts "App server is running on: http://#{$server_ip}:#{$server_port}"
set :bind, $server_ip
set :port, $server_port
get '/get_sts_token_for_oss_upload' do
token = get_sts_token_for_oss_upload()
response = {
"AccessKeyId" => token["Credentials"]["AccessKeyId"],
"AccessKeySecret" => token["Credentials"]["AccessKeySecret"],
"SecurityToken" => token["Credentials"]["SecurityToken"]
}
response.to_json
end
get '/*' do
puts "********************* GET "
send_file File.join(settings.public_folder, 'index.html')
end
C#
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.AspNetCore.Http;
using System.IO;
using System.Collections.Generic;
using System;
using System.Globalization;
using System.Text;
using System.Security.Cryptography;
using Newtonsoft.Json;
using Microsoft.AspNetCore.Http.Extensions;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
namespace YourNamespace
{
public class Program
{
private ILogger<Program> _logger;
// Konfigurasikan variabel lingkungan ALIBABA_CLOUD_ACCESS_KEY_ID.
public string AccessKeyId { get; set; } = Environment.GetEnvironmentVariable("ALIBABA_CLOUD_ACCESS_KEY_ID");
// Konfigurasikan variabel lingkungan ALIBABA_CLOUD_ACCESS_KEY_SECRET.
public string AccessKeySecret { get; set; } = Environment.GetEnvironmentVariable("ALIBABA_CLOUD_ACCESS_KEY_SECRET");
// Format host adalah bucketname.endpoint. Ganti <YOUR-BUCKET> dengan nama bucket Anda. Ganti <YOUR-ENDPOINT> dengan endpoint OSS, seperti oss-cn-hangzhou.aliyuncs.com.
public string Host { get; set; } = "<YOUR-BUCKET>.<YOUR-ENDPOINT>";
// Tentukan awalan untuk file yang diunggah ke OSS.
public string UploadDir { get; set; } = "user-dir-prefix/";
// Tentukan waktu kedaluwarsa dalam detik.
public int ExpireTime { get; set; } = 3600;
public class PolicyConfig
{
public string expiration { get; set; }
public List<List<object>> conditions { get; set; }
}
public class PolicyToken
{
public string Accessid { get; set; }
public string Policy { get; set; }
public string Signature { get; set; }
public string Dir { get; set; }
public string Host { get; set; }
public string Expire { get; set; }
}
public static void Main(string[] args)
{
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
builder.Logging.AddConsole();
var logger = builder.Services.BuildServiceProvider().GetRequiredService<ILogger<Program>>();
app.UseStaticFiles();
app.MapGet("/", async (context) =>
{
var filePath = Path.Combine(Directory.GetCurrentDirectory(), "templates/index.html");
var htmlContent = await File.ReadAllTextAsync(filePath);
await context.Response.WriteAsync(htmlContent);
logger.LogInformation("GET request to root path");
});
app.MapGet("/get_post_signature_for_oss_upload", async (context) =>
{
var program = new Program(logger);
var token = program.GetPolicyToken();
logger.LogInformation($"Token: {token}");
context.Response.ContentType = "application/json";
await context.Response.WriteAsync(token);
});
app.Run();
}
public Program(ILogger<Program> logger)
{
_logger = logger;
}
private string ToUnixTime(DateTime dateTime)
{
return ((DateTimeOffset)dateTime).ToUnixTimeSeconds().ToString();
}
private string GetPolicyToken()
{
var expireDateTime = DateTime.Now.AddSeconds(ExpireTime);
var config = new PolicyConfig
{
expiration = FormatIso8601Date(expireDateTime),
conditions = new List<List<object>>()
};
config.conditions.Add(new List<object>
{
"content-length-range", 0, 1048576000
});
var policy = JsonConvert.SerializeObject(config);
var policyBase64 = EncodeBase64("utf-8", policy);
var signature = ComputeSignature(AccessKeySecret, policyBase64);
var policyToken = new PolicyToken
{
Accessid = AccessKeyId,
Host = Host,
Policy = policyBase64,
Signature = signature,
Expire = ToUnixTime(expireDateTime),
Dir = UploadDir
};
return JsonConvert.SerializeObject(policyToken);
}
private string FormatIso8601Date(DateTime dtime)
{
return dtime.ToUniversalTime().ToString("yyyy-MM-dd'T'HH:mm:ss.fff'Z'",
CultureInfo.CurrentCulture);
}
private string EncodeBase64(string codeType, string code)
{
string encode = "";
byte[] bytes = Encoding.GetEncoding(codeType).GetBytes(code);
try
{
encode = Convert.ToBase64String(bytes);
}
catch
{
encode = code;
}
return encode;
}
private string ComputeSignature(string key, string data)
{
using (var algorithm = new HMACSHA1(Encoding.UTF8.GetBytes(key)))
{
return Convert.ToBase64String(algorithm.ComputeHash(Encoding.UTF8.GetBytes(data)));
}
}
}
}
Kode contoh sisi klien
Kode contoh berikut menunjukkan cara klien web menggunakan signature Post, Post Policy, dan informasi lain untuk mengunggah file ke OSS:
const form = document.querySelector("form");
const fileInput = document.querySelector("#file");
form.addEventListener("submit", (event) => {
event.preventDefault();
const file = fileInput.files[0];
if (!fileInput.files[0]) {
alert('Silakan pilih file untuk diunggah.');
return;
}
const filename = fileInput.files[0].name;
fetch("/get_post_signature_for_oss_upload", { method: "GET" })
.then((response) => {
if (!response.ok) {
throw new Error("Gagal mendapatkan signature.");
}
return response.json();
})
.then((data) => {
const formData = new FormData();
formData.append("name", filename);
formData.append("policy", data.policy);
formData.append("OSSAccessKeyId", data.ossAccessKeyId);
formData.append("success_action_status", "200");
formData.append("signature", data.signature);
formData.append("key", data.dir + filename);
formData.append("file", file);
return fetch(data.host, { method: "POST", body: formData });
})
.then((response) => {
if (response.ok) {
console.log("Unggah berhasil");
alert("File berhasil diunggah.");
} else {
console.log("Unggah gagal", response);
alert("Unggah gagal. Silakan coba lagi nanti.");
}
})
.catch((error) => {
console.error("Terjadi kesalahan:", error);
});
});Hasilkan URL yang ditandatangani yang diperlukan untuk PutObject di server
Proses berikut menjelaskan cara server memberikan otorisasi kepada klien untuk mengunggah file ke OSS menggunakan URL yang ditandatangani.
-
Klien meminta URL yang ditandatangani dari server aplikasi.
-
Server aplikasi menggunakan SDK OSS untuk menghasilkan URL PUT yang ditandatangani dan mengembalikannya ke klien.
-
Klien menggunakan URL PUT yang ditandatangani tersebut untuk memanggil operasi PutObject dan mengunggah file ke OSS.
-
OSS mengembalikan respons sukses ke klien.
Kode contoh
Contoh berikut menunjukkan potongan kode inti. Untuk kode lengkap, lihat proyek contoh: presignedurl.zip.
Kode contoh sisi server
Kode contoh berikut menunjukkan cara menghasilkan URL yang ditandatangani di server:
Kode ini mendukung penerapan satu klik. Anda dapat menerapkan kode ini ke Function Compute (FC) dengan satu klik.oss-upload-presigned-url-app
Python
import oss2
from oss2.credentials import EnvironmentVariableCredentialsProvider
# Dapatkan kredensial akses dari variabel lingkungan. Sebelum menjalankan kode contoh ini, pastikan variabel lingkungan OSS_ACCESS_KEY_ID dan OSS_ACCESS_KEY_SECRET telah dikonfigurasi.
auth = oss2.ProviderAuth(EnvironmentVariableCredentialsProvider())
# Ganti <YOUR_ENDPOINT> dengan endpoint wilayah tempat bucket Anda berada. Misalnya, jika bucket Anda berada di wilayah China (Hangzhou), tetapkan endpoint ke https://oss-cn-hangzhou.aliyuncs.com.
# Ganti <YOUR_BUCKET> dengan nama bucket Anda.
bucket = oss2.Bucket(auth, '<YOUR_ENDPOINT>', '<YOUR_BUCKET>')
# Tentukan waktu kedaluwarsa dalam detik.
expire_time = 3600
# Tentukan path lengkap objek, misalnya exampledir/exampleobject.png. Path lengkap tidak boleh mengandung nama bucket.
object_name = 'exampledir/exampleobject.png'
def generate_presigned_url():
# Tentukan header.
headers = dict()
# Tentukan Content-Type.
headers['Content-Type'] = 'image/png'
# Tentukan kelas penyimpanan.
# headers["x-oss-storage-class"] = "Standard"
# Saat menghasilkan URL yang ditandatangani, OSS secara default meng-escape karakter garis miring (/) dalam path lengkap objek. Hal ini membuat URL yang dihasilkan tidak dapat digunakan.
# Tetapkan slash_safe ke True. OSS tidak akan meng-escape karakter garis miring (/) dalam path lengkap objek. URL yang dihasilkan kemudian dapat digunakan secara langsung.
url = bucket.sign_url('PUT', object_name, expire_time, slash_safe=True, headers=headers)
return url
Java
import com.aliyun.oss.OSS;
import com.aliyun.oss.OSSClientBuilder;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Bean;
import com.aliyun.oss.HttpMethod;
import com.aliyun.oss.model.GeneratePresignedUrlRequest;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import java.net.URL;
import java.util.Date;
import javax.annotation.PreDestroy;
@Configuration
public class OssConfig {
/**
* Ganti <your-endpoint> dengan endpoint OSS, misalnya oss-cn-hangzhou.aliyuncs.com.
*/
private static final String endpoint = "https://oss-cn-hangzhou.aliyuncs.com";
/**
* Tetapkan accessKeyId menggunakan variabel lingkungan ALIBABA_CLOUD_ACCESS_KEY_ID.
*/
@Value("${ALIBABA_CLOUD_ACCESS_KEY_ID}")
private String accessKeyId;
/**
* Tetapkan accessKeySecret menggunakan variabel lingkungan ALIBABA_CLOUD_ACCESS_KEY_SECRET.
*/
@Value("${ALIBABA_CLOUD_ACCESS_KEY_SECRET}")
private String accessKeySecret;
private OSS ossClient;
@Bean
public OSS getSssClient() {
ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);
return ossClient;
}
@PreDestroy
public void onDestroy() {
ossClient.shutdown();
}
}
@Controller
public class PresignedURLController {
/**
* Ganti <your-bucket> dengan nama bucket Anda.
* Tentukan awalan untuk file yang diunggah ke OSS.
* Ganti <your-object> dengan path lengkap objek, misalnya exampleobject.txt. Path lengkap tidak boleh mengandung nama bucket.
* Tentukan waktu kedaluwarsa dalam milidetik.
*/
private static final String BUCKET_NAME = "<your-bucket>";
private static final String OBJECT_NAME = "<your-object>";
private static final long EXPIRE_TIME = 3600 * 1000L;
@Autowired
private OSS ossClient;
@GetMapping("/get_presigned_url_for_oss_upload")
@ResponseBody
public String generatePresignedURL() {
try {
GeneratePresignedUrlRequest request = new GeneratePresignedUrlRequest(BUCKET_NAME, OBJECT_NAME, HttpMethod.PUT);
Date expiration = new Date(System.currentTimeMillis() + EXPIRE_TIME);
request.setExpiration(expiration);
request.setContentType("image/png");
URL signedUrl = ossClient.generatePresignedUrl(request);
return signedUrl.toString();
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
Go
package main
import (
"fmt"
"net/http"
"os"
"github.com/aliyun/aliyun-oss-go-sdk/oss"
)
func getURL() string {
// Tetapkan yourEndpoint ke endpoint bucket Anda. Misalnya, jika bucket Anda berada di wilayah China (Hangzhou), tetapkan endpoint ke https://oss-cn-hangzhou.aliyuncs.com. Untuk wilayah lain, sesuaikan endpoint tersebut.
endpoint := "https://oss-cn-beijing.aliyuncs.com"
// Tentukan nama bucket, misalnya examplebucket.
bucketName := "examplebucket"
// Tentukan path lengkap file, misalnya exampledir/exampleobject.txt. Path lengkap tidak boleh mengandung nama bucket.
objectName := "exampledir/exampleobject.txt"
// Dapatkan kredensial akses dari variabel lingkungan. Sebelum menjalankan kode contoh ini, pastikan variabel lingkungan ALIBABA_CLOUD_ACCESS_KEY_ID dan ALIBABA_CLOUD_ACCESS_KEY_SECRET telah dikonfigurasi.
accessKeyID := os.Getenv("ALIBABA_CLOUD_ACCESS_KEY_ID")
accessKeySecret := os.Getenv("ALIBABA_CLOUD_ACCESS_KEY_SECRET")
client, err := oss.New(endpoint, accessKeyID, accessKeySecret)
if err != nil {
fmt.Println("json err:", err)
}
bucket, err := client.Bucket(bucketName)
if err != nil {
fmt.Println("json err:", err)
}
options := []oss.Option{
oss.ContentType("image/png"),
}
signedURL, err := bucket.SignURL(objectName, oss.HTTPPut, 60, options...)
if err != nil {
fmt.Println("json err:", err)
}
return signedURL
}
func handler(w http.ResponseWriter, r *http.Request) {
if r.URL.Path == "/" {
http.ServeFile(w, r, "templates/index.html")
return
} else if r.URL.Path == "/get_presigned_url_for_oss_upload" {
url := getURL()
fmt.Fprintf(w, "%s", url)
return
}
http.NotFound(w, r)
}
func main() {
http.HandleFunc("/", handler)
http.ListenAndServe(":8080", nil)
}
PHP
<?php
require_once __DIR__ . '/vendor/autoload.php';
use OSS\OssClient;
use OSS\Core\OssException;
use OSS\Http\RequestCore;
use OSS\Http\ResponseCore;
// Sebelum menjalankan kode contoh ini, pastikan variabel lingkungan ALIBABA_CLOUD_ACCESS_KEY_ID dan ALIBABA_CLOUD_ACCESS_KEY_SECRET telah dikonfigurasi.
$accessKeyId = getenv("ALIBABA_CLOUD_ACCESS_KEY_ID");
$accessKeySecret = getenv("ALIBABA_CLOUD_ACCESS_KEY_SECRET");
// Tetapkan yourEndpoint ke endpoint wilayah tempat bucket Anda berada. Misalnya, jika bucket Anda berada di wilayah China (Hangzhou), tetapkan endpoint ke https://oss-cn-hangzhou.aliyuncs.com.
$endpoint = "<YOUR-ENDPOINT>";
// Tentukan nama bucket.
$bucket= "<YOUR-BUCKET>";
// Tentukan path lengkap objek, tidak termasuk nama bucket.
$object = "test.png";
// Tetapkan periode validitas URL yang ditandatangani ke 3600 detik.
$timeout = 3600;
try {
$ossClient = new OssClient($accessKeyId, $accessKeySecret, $endpoint, false);
// Hasilkan URL yang ditandatangani.
$signedUrl = $ossClient->signUrl($bucket, $object, $timeout, "PUT", array('Content-Type' => 'image/png'));
// Cetak data yang dikembalikan.
echo $signedUrl;
} catch (OssException $e) {
printf($e->getMessage() . "\n");
return;
}
Node.js
const express = require("express");
const { Buffer } = require("buffer");
const OSS = require("ali-oss");
const app = express();
const path = require("path");
const fs = require("fs");
const axios = require("axios");
const config = {
// Tetapkan <YOURREGION> ke wilayah tempat bucket Anda berada. Misalnya, jika bucket Anda berada di wilayah China (Hangzhou), tetapkan region ke oss-cn-hangzhou.
region: '<YOURREGION>',
// Konfigurasikan variabel lingkungan ALIBABA_CLOUD_ACCESS_KEY_ID.
accessKeyId: process.env.ALIBABA_CLOUD_ACCESS_KEY_ID,
// Konfigurasikan variabel lingkungan ALIBABA_CLOUD_ACCESS_KEY_SECRET.
accessKeySecret: process.env.ALIBABA_CLOUD_ACCESS_KEY_SECRET,
// Ganti <YOUR-BUCKET> dengan nama bucket Anda.
bucket: "<YOUR-BUCKET>",
}
const object = "examplefile.png";
app.use(express.static(path.join(__dirname, "templates")));
app.get("/get_presigned_url_for_oss_upload", async (req, res) => {
const client = new OSS(config);
const url = client.signatureUrl(object, {
method: "PUT",
"Content-Type": "application/x-www-form-urlencoded",
});
res.send(url);
console.log(url);
});
app.listen(8000, () => {
console.log("http://127.0.0.1:8000");
});
Ruby
require 'sinatra'
require 'base64'
require 'open-uri'
require 'cgi'
require 'openssl'
require 'json'
require 'sinatra/reloader'
require 'sinatra/content_for'
require 'aliyun/oss'
include Aliyun::OSS
# Atur jalur folder publik ke folder templat di direktori saat ini.
set :public_folder, File.dirname(__FILE__) + '/templates'
# Konfigurasikan variabel lingkungan ALIBABA_CLOUD_ACCESS_KEY_ID.
$access_key_id = ENV['ALIBABA_CLOUD_ACCESS_KEY_ID']
# Konfigurasikan variabel lingkungan ALIBABA_CLOUD_ACCESS_KEY_SECRET.
$access_key_secret = ENV['ALIBABA_CLOUD_ACCESS_KEY_SECRET']
# Tentukan jalur lengkap objek, misalnya exampledir/exampleobject.png. Jalur lengkap tidak boleh berisi nama bucket.
object_key = 'exampledir/exampleobject.png'
def get_presigned_url(client, object_key)
# Ganti <YOUR-BUCKET> dengan nama bucket Anda.
bucket = client.get_bucket('<YOUR-BUCKET>')
# Hasilkan URL yang ditandatangani dan tentukan masa berlakunya selama 1 jam (3600 detik).
bucket.object_url(object_key, 3600)
end
client = Aliyun::OSS::Client.new(
# Ganti <YOUR-ENDPOINT> dengan titik akhir wilayah tempat bucket Anda berada. Misalnya, jika bucket Anda berada di wilayah Tiongkok (Hangzhou), atur titik akhir menjadi https://oss-cn-hangzhou.aliyuncs.com.
endpoint: '<YOUR-ENDPOINT>',
# Dapatkan kredensial akses dari variabel lingkungan. Sebelum menjalankan contoh kode ini, pastikan variabel lingkungan OSS_ACCESS_KEY_ID dan OSS_ACCESS_KEY_SECRET telah dikonfigurasi.
access_key_id: $access_key_id,
access_key_secret: $access_key_secret
)
if ARGV.length == 1
$server_port = ARGV[0]
elsif ARGV.length == 2
$server_ip = ARGV[0]
$server_port = ARGV[1]
end
$server_ip = "0.0.0.0"
$server_port = 8000
puts "Server aplikasi sedang berjalan di: http://#{$server_ip}:#{$server_port}"
set :bind, $server_ip
set :port, $server_port
get '/get_presigned_url_for_oss_upload' do
url = get_presigned_url(client, object_key.to_s)
puts "Token: #{url}"
url
end
get '/*' do
puts "********************* GET "
send_file File.join(settings.public_folder, 'index.html')
end
C#
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.AspNetCore.Http;
using System.IO;
using System;
using Microsoft.Extensions.Logging;
using Aliyun.OSS;
namespace YourNamespace
{
public class Program
{
private ILogger<Program> _logger;
// Konfigurasikan variabel lingkungan ALIBABA_CLOUD_ACCESS_KEY_ID.
public string AccessKeyId { get; set; } = Environment.GetEnvironmentVariable("ALIBABA_CLOUD_ACCESS_KEY_ID");
// Konfigurasikan variabel lingkungan ALIBABA_CLOUD_ACCESS_KEY_SECRET.
public string AccessKeySecret { get; set; } = Environment.GetEnvironmentVariable("ALIBABA_CLOUD_ACCESS_KEY_SECRET");
// Ganti <YOUR-ENDPOINT> dengan endpoint wilayah tempat bucket Anda berada. Misalnya, jika bucket Anda berada di wilayah China (Hangzhou), tetapkan endpoint ke https://oss-cn-hangzhou.aliyuncs.com.
private string EndPoint { get; set; } = "<YOUR-ENDPOINT>";
// Ganti <YOUR-BUCKET> dengan nama bucket Anda.
private string BucketName { get; set; } = "<YOUR-BUCKET>";
private string ObjectName { get; set; } = "exampledir/exampleobject2.png";
public static void Main(string[] args)
{
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
// Tambahkan logging.
builder.Logging.AddConsole();
var logger = builder.Services.BuildServiceProvider().GetRequiredService<ILogger<Program>>();
app.UseStaticFiles(); // Tambahkan baris ini untuk mengaktifkan middleware file statis.
app.MapGet("/", async (context) =>
{
var filePath = Path.Combine(Directory.GetCurrentDirectory(), "templates/index.html");
var htmlContent = await File.ReadAllTextAsync(filePath);
await context.Response.WriteAsync(htmlContent);
// Cetak log.
logger.LogInformation("GET request to root path");
});
app.MapGet("/get_presigned_url_for_oss_upload", async (context) =>
{
var program = new Program(logger);
var signedUrl = program.GetSignedUrl();
logger.LogInformation($"SignedUrl: {signedUrl}"); // Cetak nilai token.
await context.Response.WriteAsync(signedUrl);
});
app.Run();
}
// Konstruktor untuk injeksi ILogger.
public Program(ILogger<Program> logger)
{
_logger = logger;
}
private string GetSignedUrl()
{
// Buat instance OssClient.
var ossClient = new OssClient(EndPoint, AccessKeyId, AccessKeySecret);
// Hasilkan URL yang ditandatangani.
var generatePresignedUriRequest = new GeneratePresignedUriRequest(BucketName, ObjectName, SignHttpMethod.Put)
{
Expiration = DateTime.Now.AddHours(1),
ContentType = "image/png"
};
var signedUrl = ossClient.GeneratePresignedUri(generatePresignedUriRequest);
return signedUrl.ToString();
}
}
}
Kode contoh sisi klien
Kode contoh berikut menunjukkan cara klien web menggunakan URL yang ditandatangani untuk mengunggah file ke OSS:
const form = document.querySelector("form");
form.addEventListener("submit", (event) => {
event.preventDefault();
const fileInput = document.querySelector("#file");
const file = fileInput.files[0];
fetch("/get_presigned_url_for_oss_upload", { method: "GET" })
.then((response) => {
if (!response.ok) {
throw new Error("Gagal mendapatkan URL yang ditandatangani.");
}
return response.text();
})
.then((url) => {
fetch(url, {
method: "PUT",
headers: new Headers({
"Content-Type": "image/png",
}),
body: file,
}).then((response) => {
if (!response.ok) {
throw new Error("Gagal mengunggah file ke OSS.");
}
console.log(response);
alert("File berhasil diunggah.");
});
})
.catch((error) => {
console.error("Terjadi kesalahan:", error);
alert(error.message);
});
});Referensi praktik unggah langsung dari klien
Topik berikut menyediakan referensi untuk praktik unggah langsung bagi berbagai jenis klien: