Unggahan langsung klien memungkinkan Anda mengunggah data langsung ke Object Storage Service (OSS) dari klien. Solusi ini mempercepat proses unggah dan mengurangi penggunaan sumber daya server aplikasi dengan menghilangkan kebutuhan untuk mentransfer objek ke dan dari server aplikasi. Topik ini menjelaskan manfaat, metode implementasi, dan praktik terbaik dari solusi ini.
Manfaat
Dalam arsitektur server dan klien tradisional, mengunggah data ke OSS melalui server aplikasi adalah metode yang umum digunakan. Klien mengunggah objek ke server aplikasi, lalu server aplikasi mengunggah objek tersebut ke OSS. Selama proses ini, data harus ditransfer dua kali melalui jaringan, menyebabkan pemborosan sumber daya jaringan dan meningkatkan beban pada server aplikasi. Untuk mengatasi masalah ini, Anda dapat mengunggah objek langsung ke OSS tanpa perlu melewati server aplikasi.
Metode implementasi
Untuk mengunggah data langsung ke OSS dari klien, Anda harus menyelesaikan beberapa tantangan berikut:
CORS
Jika klien Anda adalah klien web atau mini program, Anda harus mengonfigurasi Berbagi Sumber Daya Lintas Domain (CORS). Dalam banyak kasus, browser dan mini program membatasi CORS untuk memastikan keamanan data, mencegah kode klien terhubung langsung ke OSS. Anda dapat mengonfigurasi aturan CORS untuk mengizinkan aplikasi web atau mini program pada domain tertentu mengakses OSS. Untuk informasi lebih lanjut, lihat CORS.
Otorisasi keamanan
Untuk mengunggah objek ke OSS, Anda memerlukan pasangan AccessKey dari pengguna RAM untuk penandatanganan otentikasi. Namun, jika klien menggunakan pasangan AccessKey permanen, risiko kebocoran kredensial dapat terjadi, yang mengakibatkan risiko keamanan data. Untuk mengatasi masalah ini, Anda dapat menggunakan salah satu solusi berikut untuk mengimplementasikan unggahan yang aman:
Mendapatkan Kredensial Akses Sementara dari STS di Server Aplikasi
Dalam sebagian besar skenario unggah objek, kami sarankan Anda menggunakan SDK Layanan Token Keamanan (STS) untuk mendapatkan kredensial akses sementara di server aplikasi, kemudian menggunakan kredensial tersebut bersama SDK OSS untuk mengunggah objek langsung dari klien ke OSS. Klien dapat menggunakan kembali kredensial akses sementara di server aplikasi untuk menghasilkan tanda tangan. Solusi ini cocok untuk skenario di mana unggah multipart dan unggah yang dapat dilanjutkan digunakan untuk mengunggah objek besar. Namun, panggilan STS yang sering dapat menyebabkan throttling. Dalam hal ini, kami sarankan Anda menyimpan cache kredensial akses sementara dan memperbaruinya sebelum kedaluwarsa. Untuk mencegah penyalahgunaan kredensial akses sementara di klien lebih lanjut, kami sarankan Anda mengonfigurasi kebijakan tambahan untuk membatasi penggunaannya. Untuk informasi lebih lanjut, lihat Apa itu STS?
Mendapatkan Tanda Tangan dan Kebijakan POST untuk PostObject dari Server Aplikasi
Dalam skenario di mana Anda ingin membatasi atribut objek yang akan diunggah, Anda perlu mendapatkan informasi seperti tanda tangan dan kebijakan POST dari server aplikasi. Kemudian, klien dapat menggunakan informasi ini untuk mengunggah objek langsung ke OSS tanpa menggunakan SDK OSS. Anda dapat menggunakan kebijakan yang dihasilkan oleh server aplikasi untuk membatasi atribut objek, seperti ukuran dan jenis objek. Solusi ini cocok untuk skenario di mana Anda ingin mengunggah objek menggunakan formulir HTML. Namun, solusi ini tidak mendukung unggah multipart dan unggah yang dapat dilanjutkan. Untuk informasi lebih lanjut, lihat PostObject.
Mendapatkan URL Bertanda untuk PutObject dari Server Aplikasi
Untuk skenario unggah objek sederhana, Anda dapat menggunakan SDK OSS di server aplikasi untuk mendapatkan URL bertanda yang diperlukan untuk operasi PutObject. Kemudian, klien dapat menggunakan URL bertanda untuk mengunggah objek tanpa menggunakan SDK OSS. Solusi ini tidak cocok untuk unggah multipart dan unggah yang dapat dilanjutkan. Server aplikasi menghasilkan URL bertanda untuk setiap bagian dan mengembalikannya ke klien, meningkatkan jumlah interaksi dengan server aplikasi serta kompleksitas permintaan jaringan. Selain itu, klien dapat memodifikasi konten atau urutan bagian, yang menghasilkan objek gabungan yang tidak valid. Untuk informasi lebih lanjut, lihat Sertakan Tanda Tangan V1 dalam URL.
Mendapatkan kredensial akses sementara dari STS di server aplikasi
Gambar berikut menunjukkan cara mengunggah objek ke OSS dari klien menggunakan kredensial akses sementara STS yang dikembalikan dari server aplikasi.
Klien meminta kredensial akses sementara dari server aplikasi.
Server aplikasi menggunakan SDK STS untuk memanggil operasi AssumeRole guna mendapatkan kredensial akses sementara.
STS menghasilkan dan mengembalikan kredensial akses sementara ke server aplikasi.
Server aplikasi mengembalikan kredensial akses sementara ke klien.
Klien menggunakan SDK OSS untuk mengunggah objek ke OSS dengan kredensial akses sementara.
OSS mengembalikan respons sukses ke klien.
Contoh
Contoh berikut hanya menyediakan blok kode inti. Untuk sampel kode lengkap, unduh paket sts.zip.
Kode Contoh Sisi Server
Kode contoh berikut menunjukkan cara server aplikasi mendapatkan kredensial akses sementara dari STS:
Kode contoh dapat diterapkan di Function Compute. 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 Alibaba Cloud Resource Name (ARN) dari peran RAM yang memiliki izin untuk mengunggah objek ke bucket OSS.
role_arn_for_oss_upload = '<YOUR_ROLE_ARN>'
# Ganti <YOUR_REGION_ID> dengan ID wilayah layanan STS. Contoh: cn-hangzhou.
region_id = '<YOUR_REGION_ID>'
def get_sts_token():
# Jika Anda tidak menentukan parameter saat menginisialisasi CredentialClient, rantai kredensial default akan digunakan.
# Jika Anda menjalankan program di komputer lokal, Anda dapat memperoleh pasangan AccessKey dari variabel lingkungan ALIBABA_CLOUD_ACCESS_KEY_ID dan ALIBABA_CLOUD_ACCESS_KEY_SECRET.
# Jika Anda menjalankan program di Elastic Compute Service (ECS) atau Elastic Container Instance, Anda dapat menentukan peran instance yang terikat dengan mengonfigurasi variabel lingkungan ALIBABA_CLOUD_ECS_METADATA. SDK secara otomatis memperoleh kredensial akses sementara dari 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,
# Ganti <YOUR_ROLE_SESSION_NAME> dengan nama sesi kustom Anda, 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)
// Ganti <YOUR_ROLE_SESSION_NAME> dengan nama sesi kustom Anda, seperti my-website-server.
.setRoleSessionName("<YOUR_ROLE_SESSION_NAME>")
// Ganti <YOUR_ROLE_ARN> dengan ARN dari peran RAM yang memiliki izin untuk mengunggah objek ke bucket tertentu. Anda dapat memperoleh ARN dari peran RAM di halaman detail peran RAM.
RuntimeOptions runtime = new RuntimeOptions();
try {
AssumeRoleResponse response = stsClient.assumeRoleWithOptions(assumeRoleRequest, runtime);
return response.body.credentials;
} catch (TeaException error) {
// Cetak kesalahan jika perlu.
assertAsString(error.message);
return null;
} catch (Exception error) {
TeaException error = new TeaException(_error.getMessage(), _error);
// Cetak kesalahan jika perlu.
assertAsString(error.message);
return null;
}
}
}
@Configuration
public class StsClientConfiguration {
@Bean
public Client stsClient() {
// Jika Anda tidak menentukan parameter apa pun saat menginisialisasi klien kredensial, Alat Kredensial 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"
)
/**
* Gunakan AccessKey ID dan AccessKey secret Anda untuk menginisialisasi klien Anda.
* @param accessKeyId
* @param accessKeySecret
* @return Client
* @throws Exception
*/
func CreateClient(accessKeyId *string, accessKeySecret *string) (*sts20150401.Client, error) {
config := &openapi.Config{
// (Diperlukan) Tentukan AccessKey ID Anda.
AccessKeyId: accessKeyId,
// (Diperlukan) Tentukan AccessKey secret Anda.
AccessKeySecret: accessKeySecret,
}
// Untuk informasi lebih lanjut tentang titik akhir, kunjungi 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 AlibabaCloud.
AlibabaCloud::accessKeyClient(getenv('ALIBABA_CLOUD_ACCESS_KEY_ID'), getenv('ALIBABA_CLOUD_ACCESS_KEY_SECRET'))
->regionId('cn-hangzhou')
->asDefaultClient();
// Buat permintaan untuk mendapatkan kredensial akses sementara dari STS.
$request = Sts::v20150401()->assumeRole();
// Kirim permintaan STS dan dapatkan hasilnya.
// Ganti <YOUR_ROLE_SESSION_NAME> dengan nama sesi kustom Anda, seperti oss-role-session.
// Ganti <YOUR_ROLE_ARN> dengan ARN dari peran RAM yang memiliki izin untuk mengunggah objek ke bucket tertentu.
$result = $request
->withRoleSessionName("<YOUR_ROLE_SESSION_NAME>")
->withDurationSeconds(3600)
->withRoleArn("<YOUR_ROLE_ARN>")
->request();
// Dapatkan informasi kredensial dalam hasil permintaan STS.
$credentials = $result->get('Credentials');
// Konstruksi data JSON dari hasil yang dikembalikan.
$response = [
'AccessKeyId' => $credentials['AccessKeyId'],
'AccessKeySecret' => $credentials['AccessKeySecret'],
'SecurityToken' => $credentials['SecurityToken'],
];
// Tetapkan header respons Content-Type ke application/json.
header('Content-Type: application/json');
// Ubah hasil menjadi data JSON dan cetak hasilnya.
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")));
// Dapatkan AccessKey ID dari variabel lingkungan ALIBABA_CLOUD_ACCESS_KEY_ID.
const accessKeyId = process.env.ALIBABA_CLOUD_ACCESS_KEY_ID;
// Dapatkan AccessKey secret dari variabel lingkungan ALIBABA_CLOUD_ACCESS_SECRET.
const accessKeySecret = process.env.ALIBABA_CLOUD_ACCESS_SECRET;
app.get('/get_sts_token_for_oss_upload', (req, res) => {
let sts = new STS({
accessKeyId: accessKeyId,
accessKeySecret: accessKeySecret
});
// Tentukan ARN dari peran yang diperoleh di Langkah 2. Contoh: acs:ram::175708322470****:role/ramtest.
// Tentukan kebijakan kustom untuk membatasi izin kredensial akses sementara. Jika Anda tidak menentukan kebijakan kustom, kredensial akses sementara yang dikembalikan memiliki izin penuh dari peran RAM yang ditentukan.
// Tentukan periode validitas kredensial akses sementara. Dalam contoh ini, periode validitas diatur menjadi 3.000 detik.
// Tentukan nama sesi peran kustom, yang digunakan untuk membedakan token yang berbeda. Contoh: 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'
# Setel jalur folder publik ke subfolder templates dalam folder saat ini.
set :public_folder, File.dirname(__FILE__) + '/templates'
def get_sts_token_for_oss_upload()
client = RPCClient.new(
# Dapatkan AccessKey ID dari variabel lingkungan ALIBABA_CLOUD_ACCESS_KEY_ID.
access_key_id: ENV['ALIBABA_CLOUD_ACCESS_KEY_ID'],
# Dapatkan AccessKey secret dari 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: {
# Tentukan ARN dari peran RAM yang diperoleh di Langkah 2. Contoh: acs:ram::175708322470****:role/ramtest.
"RoleArn": "acs:ram::175708322470****:role/ramtest",
# Tentukan periode validitas kredensial akses sementara. Dalam contoh ini, periode validitas diatur menjadi 3.600 detik.
"DurationSeconds": 3600,
# Tentukan nama sesi peran kustom, yang digunakan untuk membedakan token yang berbeda. Contoh: 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')
endC#
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();
// Ganti <YOUR_ROLE_SESSION_NAME> dengan nama sesi kustom Anda, seperti oss-role-session.
assumeRoleRequest.RoleSessionName = "<YOUR_ROLE_SESSION_NAME>";
// Ganti <YOUR_ROLE_ARN> dengan ARN dari peran RAM yang memiliki izin untuk mengunggah objek ke bucket 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 menggunakan kredensial akses sementara untuk mengunggah objek ke OSS dari klien web:
let credentials = null;
const form = document.querySelector("form");
form.addEventListener("submit", async (event) => {
event.preventDefault();
// Dapatkan kembali kredensial akses sementara hanya jika mereka kedaluwarsa. Ini mengurangi jumlah panggilan ke STS.
if (isCredentialsExpired(credentials)) {
const response = await fetch("/get_sts_token_for_oss_upload", {
method: "GET",
});
if (!response.ok) {
// Tangani kode kesalahan HTTP.
throw new Error(
'Gagal mendapatkan token STS: ${response.status} ${response.statusText}'
);
}
credentials = await response.json();
}
const client = new OSS({
// Ganti <YOUR_BUCKET> dengan nama bucket OSS.
bucket: "<YOUR_BUCKET>",
// Ganti <YOUR_REGION> dengan ID wilayah tempat bucket OSS berada. Contoh: 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);
});
/**
* Tentukan apakah kredensial akses sementara kedaluwarsa.
**/
function isCredentialsExpired(credentials) {
if (!credentials) {
return true;
}
const expireDate = new Date(credentials.Expiration);
const now = new Date();
// Jika masa berlaku kredensial akses sementara kurang dari 1 menit, kredensial akses sementara dianggap kedaluwarsa.
return expireDate.getTime() - now.getTime() <= 60000;
}Mendapatkan tanda tangan dan kebijakan POST untuk PostObject dari server aplikasi
Gambar berikut menunjukkan cara mengunggah objek dari klien ke OSS menggunakan tanda tangan dan kebijakan POST yang diperoleh dari server aplikasi.
Klien meminta informasi seperti tanda tangan dan kebijakan POST dari server aplikasi.
Server aplikasi menghasilkan dan mengembalikan informasi seperti tanda tangan dan kebijakan POST ke klien.
Klien menggunakan informasi seperti tanda tangan dan kebijakan POST untuk memanggil operasi PostObject guna mengunggah objek ke OSS menggunakan formulir HTML.
OSS mengembalikan respons sukses ke klien.
Contoh
Contoh berikut hanya menyediakan blok kode inti. Untuk sampel kode lengkap, unduh paket postsignature.zip.
Kode Contoh Sisi Server
Kode contoh berikut menunjukkan cara server aplikasi menghasilkan tanda tangan dan kebijakan POST untuk PostObject:
Kode contoh dapat diterapkan di Function Compute. oss-upload-post-signature-app
Python
import os
from hashlib import sha1 as sha
import json
import base64
import hmac
import datetime
import time
# Dapatkan AccessKey ID dari variabel lingkungan OSS_ACCESS_KEY_ID.
access_key_id = os.environ.get('OSS_ACCESS_KEY_ID')
# Dapatkan AccessKey secret dari variabel lingkungan OSS_ACCESS_KEY_SECRET.
access_key_secret = os.environ.get('OSS_ACCESS_KEY_SECRET')
# Ganti <YOUR_BUCKET> dengan nama bucket.
bucket = '<YOUR_BUCKET>'
# Setel host ke nilai dalam format BucketName.Endpoint. Ganti <YOUR_BUCKET> dengan nama bucket. Ganti <YOUR_ENDPOINT> dengan titik akhir wilayah tempat bucket berada, seperti oss-cn-hangzhou.aliyuncs.com.
host = 'https://<YOUR_BUCKET>.<YOUR_ENDPOINT>'
# Tentukan awalan dalam nama objek yang ingin Anda unggah ke OSS.
upload_dir = 'user-dir-prefix/'
# Tentukan periode validitas. Satuan: detik.
expire_time = 3600
def generate_expiration(seconds):
"""
Waktu kedaluwarsa dihitung berdasarkan periode validitas yang ditentukan. Satuan: detik.
:param seconds: periode validitas (detik).
:return: string waktu dalam standar ISO 8601. Contoh: 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 tanda tangan.
:param access_key_secret: AccessKey secret yang memiliki izin untuk mengakses bucket.
:param expiration: waktu ketika tanda tangan kedaluwarsa. Tentukan waktu dalam standar ISO 8601 dalam format yyyy-MM-ddTHH:mm:ssZ. Contoh: 2014-12-01T12:00:00.000Z.
:param conditions: kondisi kebijakan yang dapat digunakan untuk membatasi nilai yang dapat Anda tentukan selama unggah formulir.
:param policy_extra_props: parameter kebijakan tambahan. Anda dapat melewati parameter tambahan sebagai dict.
:return: string tanda tangan.
"""
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 = {
# Tentukan periode validitas.
"expiration": generate_expiration(expire_time),
# Tentukan kondisi kebijakan.
"conditions": [
# Jika success_action_redirect tidak ditentukan, kode status HTTP 204 dikembalikan setelah objek diunggah.
["eq", "$success_action_status", "200"],
# Nilai bidang formulir harus dimulai dengan awalan yang ditentukan. Misalnya, jika nilai bidang kunci dimulai dengan user/user1, kondisinya adalah ["starts-with", "$key", "user/user1"].
["starts-with", "$key", upload_dir],
# Tentukan ukuran minimum dan maksimum objek yang dapat diunggah. Satuan: byte.
["content-length-range", 1, 1000000],
# Tentukan jenis file yang diizinkan.
["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
# Tentukan parameter tambahan.
}
return json.dumps(response)
Java
import com.aliyun.help.demo.uploading_to_oss_directly_postsignature.config.OssConfig;
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;
import com.aliyun.oss.OSSClientBuilder;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Bean;
import javax.annotation.PreDestroy;
@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("Tangkap OSSException, yang berarti permintaan Anda sampai di OSS, "
+ "tetapi ditolak dengan respons kesalahan karena alasan tertentu.");
// Asumsikan metode ini ada.
System.out.println("Kode Status HTTP: " + oe.getRawResponseError());
System.out.println("Pesan Kesalahan: " + oe.getErrorMessage());
System.out.println("Kode Kesalahan: " + oe.getErrorCode());
System.out.println("ID Permintaan: " + oe.getRequestId());
System.out.println("ID Host: " + oe.getHostId());
} catch (ClientException ce) {
System.out.println("Tangkap ClientException, yang berarti klien mengalami "
+ "masalah internal serius saat mencoba berkomunikasi dengan OSS, "
+ "seperti tidak dapat mengakses jaringan.");
System.out.println("Pesan Kesalahan: " + ce.getMessage());
} finally {
if (ossClient != null) {
ossClient.shutdown();
}
return response.toString();
}
}
}
@Configuration
public class OssConfig {
/**
* Ganti <YOUR-ENDPOINT> dengan titik akhir wilayah tempat bucket berada, seperti oss-cn-hangzhou.aliyuncs.com.
*/
private String endpoint = "<YOUR-ENDPOINT>";
/**
* Ganti <YOUR-BUCKET> dengan nama bucket.
*/
private String bucket = "<YOUR-BUCKET>";
/**
* Tentukan awalan dalam nama objek di OSS.
*/
private String dir = "user-dir-prefix/";
/**
* Tentukan periode validitas. Satuan: detik.
*/
private long expireTime = 3600;
/**
* Buat host.
*/
private String host = "http://" + bucket + "." + endpoint;
/**
* Dapatkan AccessKey ID dari variabel lingkungan ALIBABA_CLOUD_ACCESS_KEY_ID.
*/
private String accessKeyId = System.getenv("ALIBABA_CLOUD_ACCESS_KEY_ID");
/**
* Dapatkan AccessKey secret dari variabel lingkungan ALIBABA_CLOUD_ACCESS_KEY_SECRET.
*/
private String accessKeySecret = System.getenv("ALIBABA_CLOUD_ACCESS_KEY_SECRET");
private OSS ossClient;
@Bean
public OSS getOssClient() {
ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);
return ossClient;
}
@Bean
public String getHost() {
return host;
}
@Bean
public String getAccessKeyId() {
return accessKeyId;
}
@Bean
public long getExpireTime() {
return expireTime;
}
@Bean
public String getDir() {
return dir;
}
@PreDestroy
public void onDestroy() {
ossClient.shutdown();
}
}
Go
package main
import (
"crypto/hmac"
"crypto/sha1"
"encoding/base64"
"encoding/json"
"fmt"
"io"
"net/http"
"os"
"time"
)
var (
// Dapatkan AccessKey ID dari variabel lingkungan OSS_ACCESS_KEY_ID.
accessKeyId = os.Getenv("OSS_ACCESS_KEY_ID")
// Dapatkan AccessKey secret dari variabel lingkungan OSS_ACCESS_KEY_SECRET.
accessKeySecret = os.Getenv("OSS_ACCESS_KEY_SECRET")
// Tentukan host. Format: BucketName.Endpoint. Ganti ${your-bucket} dengan nama bucket. Ganti ${your-endpoint} dengan titik akhir OSS, seperti oss-cn-hangzhou.aliyuncs.com.
host = "http://${your-bucket}.${your-endpoint}"
// Tentukan awalan dalam nama objek yang ingin Anda unggah ke OSS.
uploadDir = "user-dir-prefix/"
// Tentukan periode validitas. Satuan: 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
var condition []string
condition = append(condition, "starts-with")
condition = append(condition, "$key")
condition = append(condition, uploadDir)
config.Conditions = append(config.Conditions, condition)
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, 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");
// Tentukan host. Format: <YOUR-BUCKET>.<YOUR-ENDPOINT>.
$host = 'http://<YOUR-BUCKET>.<YOUR-ENDPOINT>';
// Tentukan awalan dalam nama objek yang ingin Anda unggah.
$dir = 'user-dir-prefix/';
$now = time();
// Tentukan waktu kedaluwarsa kebijakan. Jika waktu kedaluwarsa terlewati, akses akan ditolak. Dalam contoh ini, waktu kedaluwarsa diatur menjadi 10 detik.
$expire = 30;
$end = $now + $expire;
$expiration = gmt_iso8601($end);
// Tentukan ukuran maksimum objek yang dapat diunggah.
$condition = array(0 => 'content-length-range', 1 => 0, 2 => 1048576000);
$conditions[] = $condition;
// Tentukan objek yang dapat diunggah. Dalam contoh ini, hanya objek yang namanya dimulai dengan nilai parameter $dir yang dapat diunggah. Ini adalah pengaturan opsional yang mencegah objek diunggah ke direktori berbeda di bucket menggunakan 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 = {
// Dapatkan AccessKey ID dari variabel lingkungan ALIBABA_CLOUD_ACCESS_KEY_ID.
accessKeyId: process.env.ALIBABA_CLOUD_ACCESS_KEY_ID,
// Dapatkan AccessKey secret dari variabel lingkungan ALIBABA_CLOUD_ACCESS_KEY_SECRET.
accessKeySecret: process.env.ALIBABA_CLOUD_ACCESS_KEY_SECRET,
// Ganti <YOUR-BUCKET> dengan nama bucket.
bucket: "<YOUR-BUCKET>",
// Tentukan awalan dalam nama objek yang ingin Anda unggah 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();
// Tentukan periode validitas tanda tangan. Satuan: detik.
date.setSeconds(date.getSeconds() + 3600);
const policy = {
expiration: date.toISOString(),
conditions: [
// Tentukan batas ukuran objek yang dapat diunggah.
["content-length-range", 0, 1048576000],
// Tentukan bucket ke mana objek 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'
# Setel jalur folder publik ke subfolder templates dalam folder saat ini.
set :public_folder, File.dirname(__FILE__) + '/templates'
# Dapatkan AccessKey ID dari variabel lingkungan ALIBABA_CLOUD_ACCESS_KEY_ID.
$access_key_id = ENV['ALIBABA_CLOUD_ACCESS_ID']
# Dapatkan AccessKey secret dari variabel lingkungan ALIBABA_CLOUD_ACCESS_KEY_SECRET.
$access_key_secret = ENV['ALIBABA_CLOUD_ACCESS_SECRET']
# Tentukan host. Host dalam format <bucketname>.<endpoint>.
$host = 'http://<bucketname>.<endpoint>';
# Tentukan awalan dalam nama objek yang ingin Anda unggah.
$upload_dir = 'user-dir-prefix/'
# Tentukan periode validitas. Satuan: 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;
// Dapatkan AccessKey ID dari variabel lingkungan ALIBABA_CLOUD_ACCESS_KEY_ID.
public string AccessKeyId { get; set; } = Environment.GetEnvironmentVariable("ALIBABA_CLOUD_ACCESS_KEY_ID");
// Dapatkan AccessKey secret dari variabel lingkungan ALIBABA_CLOUD_ACCESS_KEY_SECRET.
public string AccessKeySecret { get; set; } = Environment.GetEnvironmentVariable("ALIBABA_CLOUD_ACCESS_KEY_SECRET");
// Tentukan host. Format: BucketName.Endpoint. Ganti <YOUR-BUCKET> dengan nama bucket. Ganti <YOUR-ENDPOINT> dengan titik akhir wilayah tempat bucket berada, seperti oss-cn-hangzhou.aliyuncs.com.
public string Host { get; set; } = "<YOUR-BUCKET>.<YOUR-ENDPOINT>";
// Tentukan awalan dalam nama objek yang ingin Anda unggah ke OSS.
public string UploadDir { get; set; } = "user-dir-prefix/";
// Tentukan periode validitas. Satuan: 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 menggunakan tanda tangan dan kebijakan POST untuk mengunggah objek ke OSS dari klien web:
const form = document.querySelector("form");
const fileInput = document.querySelector("#file");
form.addEventListener("submit", (event) => {
event.preventDefault();
const file = fileInput.files[0];
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 tanda tangan");
}
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("Diunggah");
alert("Objek diunggah");
} else {
console.log("Unggah gagal", response);
alert("Gagal mengunggah objek. Coba unggah ulang objek");
}
})
.catch((error) => {
console.error("Terjadi kesalahan:", error);
});
});Mendapatkan URL bertanda untuk PutObject dari server aplikasi
Gambar berikut menunjukkan cara mengunggah objek ke OSS dari klien menggunakan URL bertanda yang diperoleh dari server aplikasi.
Klien meminta URL bertanda dari server aplikasi.
Server aplikasi menggunakan SDK OSS untuk menghasilkan URL bertanda untuk permintaan PUT dan mengembalikannya ke klien.
Klien menggunakan URL bertanda untuk memanggil operasi PutObject guna mengunggah objek ke OSS.
OSS mengembalikan respons sukses ke klien.
Contoh
Contoh berikut hanya menyediakan blok kode inti. Untuk sampel kode lengkap, unduh paket presignedurl.zip.
Kode Contoh Sisi Server
Kode contoh berikut menunjukkan cara mendapatkan URL bertanda dari server aplikasi:
Kode contoh dapat diterapkan di Function Compute. oss-upload-presigned-url-app
Python
import oss2
from oss2.credentials import EnvironmentVariableCredentialsProvider
# Dapatkan kredensial akses dari variabel lingkungan. Sebelum menjalankan kode contoh, pastikan variabel lingkungan OSS_ACCESS_KEY_ID dan OSS_ACCESS_KEY_SECRET telah dikonfigurasi.
auth = oss2.ProviderAuth(EnvironmentVariableCredentialsProvider())
# Ganti <YOUR_ENDPOINT> dengan titik akhir wilayah tempat bucket berada. Contohnya, jika bucket berada di wilayah China (Hangzhou), atur titik akhir menjadi https://oss-cn-hangzhou.aliyuncs.com.
# Ganti <YOUR_BUCKET> dengan nama bucket.
bucket = oss2.Bucket(auth, '<YOUR_ENDPOINT>', '<YOUR_BUCKET>')
# Tentukan periode validitas. Satuan: detik.
expire_time = 3600
# Tentukan jalur lengkap objek. Contoh: exampledir/exampleobject.png. Jangan sertakan nama bucket dalam jalur lengkap.
object_name = 'exampledir/exampleobject.png'
def generate_presigned_url():
# Tentukan header.
headers = dict()
# Tentukan header Content-Type.
headers['Content-Type'] = 'image/png'
# Tentukan kelas penyimpanan.
# headers["x-oss-storage-class"] = "Standard"
# Secara default, OSS mengidentifikasi garis miring (/) dalam jalur lengkap objek sebagai karakter escape saat URL bertanda dihasilkan. Oleh karena itu, Anda tidak dapat menggunakan URL bertanda secara langsung.
# Atur parameter slash_safe menjadi True. Dengan cara ini, OSS tidak mengidentifikasi garis miring (/) dalam jalur lengkap objek sebagai karakter escape. Dalam hal ini, Anda dapat menggunakan URL bertanda yang dihasilkan untuk mengunggah objek.
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 titik akhir wilayah tempat bucket berada, seperti oss-cn-hangzhou.aliyuncs.com.
*/
private static final String endpoint = "https://oss-cn-hangzhou.aliyuncs.com";
/**
* Dapatkan AccessKey ID dari variabel lingkungan ALIBABA_CLOUD_ACCESS_KEY_ID.
*/
@Value("${ALIBABA_CLOUD_ACCESS_KEY_ID}")
private String accessKeyId;
/**
* Dapatkan AccessKey secret dari 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.
* Tentukan awalan dalam nama objek yang ingin Anda unggah ke OSS.
* Ganti <your-object> dengan jalur lengkap objek. Contoh: exampleobject.txt. Jangan sertakan nama bucket dalam jalur lengkap.
* Tentukan waktu kedaluwarsa. Satuan: 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 {
// Tentukan titik akhir wilayah tempat bucket berada. Contohnya, jika bucket berada di wilayah China (Hangzhou), atur titik akhir menjadi https://oss-cn-hangzhou.aliyuncs.com. Tentukan titik akhir sebenarnya Anda.
endpoint := "https://oss-cn-beijing.aliyuncs.com"
// Tentukan nama bucket. Contoh: examplebucket.
bucketName := "examplebucket"
// Tentukan jalur lengkap objek. Contoh: exampledir/exampleobject.txt. Jangan sertakan nama bucket dalam jalur lengkap.
objectName := "exampledir/exampleobject.txt"
// Dapatkan kredensial akses dari variabel lingkungan. Sebelum menjalankan kode contoh, 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, 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");
// Tentukan titik akhir wilayah tempat bucket berada. Contohnya, jika bucket berada di wilayah China (Hangzhou), atur titik akhir menjadi https://oss-cn-hangzhou.aliyuncs.com.
$endpoint = "<YOUR-ENDPOINT>";
// Tentukan nama bucket.
$bucket= "<YOUR-BUCKET>";
// Tentukan jalur lengkap objek. Jangan sertakan nama bucket dalam jalur lengkap.
$object = "test.png";
// Atur periode validitas URL bertanda menjadi 3.600 detik.
$timeout = 3600;
try {
$ossClient = new OssClient($accessKeyId, $accessKeySecret, $endpoint, false);
// Hasilkan URL bertanda.
$signedUrl = $ossClient->signUrl($bucket, $object, $timeout, "PUT", array('Content-Type' => 'image/png'));
// Tampilkan 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 = {
// Tentukan wilayah tempat bucket berada. Contohnya, jika bucket berada di wilayah China (Hangzhou), atur wilayah menjadi oss-cn-hangzhou.
region: '<YOURREGION>',
// Dapatkan AccessKey ID dari variabel lingkungan ALIBABA_CLOUD_ACCESS_KEY_ID.
accessKeyId: process.env.ALIBABA_CLOUD_ACCESS_KEY_ID,
// Dapatkan AccessKey secret dari variabel lingkungan ALIBABA_CLOUD_ACCESS_KEY_SECRET.
accessKeySecret: process.env.ALIBABA_CLOUD_ACCESS_KEY_SECRET,
// Ganti <YOUR-BUCKET> dengan nama bucket.
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
# Setel jalur folder publik ke subfolder templates dalam folder saat ini.
set :public_folder, File.dirname(__FILE__) + '/templates'
# Dapatkan AccessKey ID dari variabel lingkungan ALIBABA_CLOUD_ACCESS_KEY_ID.
$access_key_id = ENV['ALIBABA_CLOUD_ACCESS_KEY_ID']
# Dapatkan AccessKey secret dari variabel lingkungan ALIBABA_CLOUD_ACCESS_SECRET.
$access_key_secret = ENV['ALIBABA_CLOUD_ACCESS_KEY_SECRET']
# Tentukan jalur lengkap objek. Contoh: exampledir/exampleobject.png. Jangan sertakan nama bucket dalam jalur lengkap.
object_key = 'exampledir/exampleobject.png'
def get_presigned_url(client, object_key)
# Ganti <YOUR-BUCKET> dengan nama bucket.
bucket = client.get_bucket('<YOUR-BUCKET>')
# Hasilkan URL bertanda dan atur periode validitas URL menjadi 3600. Satuan: detik.
bucket.object_url(object_key, 3600)
end
client = Aliyun::OSS::Client.new(
# Tentukan titik akhir wilayah tempat bucket berada. Contohnya, jika bucket berada di wilayah China (Hangzhou), atur titik akhir menjadi https://oss-cn-hangzhou.aliyuncs.com.
endpoint: '<YOUR-ENDPOINT>',
# Dapatkan kredensial akses dari variabel lingkungan. Sebelum menjalankan kode contoh, 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 "App server is running on: 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;
// Dapatkan AccessKey ID dari variabel lingkungan ALIBABA_CLOUD_ACCESS_KEY_ID.
public string AccessKeyId { get; set; } = Environment.GetEnvironmentVariable("ALIBABA_CLOUD_ACCESS_KEY_ID");
// Dapatkan AccessKey secret dari variabel lingkungan ALIBABA_CLOUD_ACCESS_KEY_SECRET.
public string AccessKeySecret { get; set; } = Environment.GetEnvironmentVariable("ALIBABA_CLOUD_ACCESS_KEY_SECRET");
// Ganti <YOUR-ENDPOINT> dengan titik akhir wilayah tempat bucket berada. Contohnya, jika bucket berada di wilayah China (Hangzhou), atur titik akhir menjadi https://oss-cn-hangzhou.aliyuncs.com.
private string EndPoint { get; set; } = "<YOUR-ENDPOINT>";
// Ganti <YOUR-BUCKET> dengan nama bucket.
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();
// Tentukan 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);
// Tampilkan 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}"); // Tampilkan nilai token.
await context.Response.WriteAsync(signedUrl);
});
app.Run();
}
// Injeksi ILogger.
public Program(ILogger<Program> logger)
{
_logger = logger;
}
private string GetSignedUrl()
{
// Buat instance OSSClient.
var ossClient = new OssClient(EndPoint, AccessKeyId, AccessKeySecret);
// Buat permintaan untuk menghasilkan URL bertanda.
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 menggunakan URL bertanda untuk mengunggah objek ke OSS dari klien web:
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 bertanda");
}
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 objek ke OSS");
}
console.log(response);
alert("Objek diunggah");
});
})
.catch((error) => {
console.error("Terjadi kesalahan:", error);
alert(error.message);
});
});Praktik terbaik
Untuk informasi tentang praktik terbaik dalam mengunggah data ke OSS dari klien, lihat topik berikut: