Form upload lets you directly upload objects to Object Storage Service (OSS) using standard HTML forms from web applications. This topic describes how to generate information such as a signature and an upload policy using OSS SDK forn PHP 2.0 and use the HTTP Post method to upload an object to OSS.
Notes
The sample code in this topic uses the region ID
cn-hangzhouof the China (Hangzhou) region. By default, a public endpoint is used to access resources in a bucket. If you want to access resources in the bucket from other Alibaba Cloud services in the same region, use an internal endpoint. For more information about supported regions and endpoints, see OSS regions and endpoints.The size of the object that you want to upload using form upload cannot exceed 5 GB.
In this topic, access credentials are obtained from environment variables. For further instructions, see Configure access credentials in PHP.
Sample code
The following sample code demonstrates how to perform form upload. The complete process of form upload consists of the following steps:
Create a Post Policy: Specify the validity period and conditions of an upload request, including the bucket name, signature version, credentials, request date, and length range of the request body.
Process the upload policy: Convert the upload policy to a JSON string and Base64-encode the string.
Generate a signing key: Use the HMAC-SHA256 algorithm to generate a signing key including the date, region, service, and request type.
Calculate signature: Use the generated key to sign the Base64 encoded policy string and convert the signature to a hexadecimal string.
Construct a request body: Create a multipart form writer, specify the object key, policy, signature version, credentials, request date, and signature in the form, and write the data that you want to upload to the form.
Create and execute a request: Create an HTTP POST request and specify the request headers. Send the request and check the response status code to ensure that the request succeeds.
<?php
// Include the autoload file to load dependencies.
require_once __DIR__ . '/../vendor/autoload.php';
use AlibabaCloud\Oss\V2 as Oss;
// Define and describe command-line parameters.
$optsdesc = [
"region" => ['help' => 'The region in which the bucket is located.', 'required' => True], // (Required)Region in which the bucket is located.
"bucket" => ['help' => 'The name of the bucket', 'required' => True], // (Required) Bucket name.
"key" => ['help' => 'The name of the object', 'required' => True], // (Required) Object name.
];
// Convert the descriptions to a list of long options required by getopt.
// Add a colon (:) to the end of each parameter to indicate that a value is required.
$longopts = \array_map(function ($key) {
return "$key:";
}, array_keys($optsdesc));
// Parse the command-line parameters.
$options = getopt("", $longopts);
// Check whether the required parameters are configured.
foreach ($optsdesc as $key => $value) {
if ($value['required'] === True && empty($options[$key])) {
$help = $value['help']; // Obtain help information for the parameters.
echo "Error: the following arguments are required: --$key, $help" . PHP_EOL;
exit(1); // Exit the program if required parameters are missing
}
}
// Extract values from parsed parameters
$region = $options["region"]; // The region where the bucket is located
$bucket = $options["bucket"]; // Bucket name
$key = $options["key"]; // Object name
$product = 'oss'; // Fixed as OSS service
// Load credential information from environment variables
// Use EnvironmentVariableCredentialsProvider to read Access Key ID and Access Key Secret from environment variables
$credentialsProvider = new Oss\Credentials\EnvironmentVariableCredentialsProvider();
$cred = $credentialsProvider->getCredentials();
// Data content to be uploaded
$data = 'hi oss'; // Example data, can be replaced with other content in actual use
// Get current UTC time and format it
$utcTime = new DateTime('now', new DateTimeZone('UTC'));
$date = $utcTime->format('Ymd'); // Current date, used for signature calculation
$expiration = clone $utcTime;
$expiration->add(new DateInterval('PT1H')); // Set expiration time to 1 hour later
// Build Policy document
$policyMap = [
"expiration" => $expiration->format('Y-m-d\TH:i:s.000\Z'), // Policy expiration time
"conditions" => [
["bucket" => $bucket], // Specify bucket name
["x-oss-signature-version" => "OSS4-HMAC-SHA256"], // Signature version
["x-oss-credential" => sprintf("%s/%s/%s/%s/aliyun_v4_request",
$cred->getAccessKeyId(), $date, $region, $product)], // Credential information
["x-oss-date" => $utcTime->format('Ymd\THis\Z')], // Current timestamp
// Other conditions
["content-length-range", 1, 1024], // File size range limit
// ["eq", "$success_action_status", "201"], // Optional: specify success status code
// ["starts-with", "$key", "user/eric/"], // Optional: specify object key prefix
// ["in", "$content-type", ["image/jpg", "image/png"]], // Optional: specify allowed content types
// ["not-in", "$cache-control", ["no-cache"]], // Optional: exclude certain cache control headers
],
];
// Encode Policy document as JSON string
$jsonOptions = JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE; // Prevent escaping slashes and Unicode characters
$policy = json_encode($policyMap, $jsonOptions);
if (json_last_error() !== JSON_ERROR_NONE) {
error_log("json_encode fail, err: " . json_last_error_msg()); // Check if JSON encoding failed
exit(1);
}
// Calculate information needed for signature
$stringToSign = base64_encode($policy); // Base64 encode the Policy
$signingKey = "aliyun_v4" . $cred->getAccessKeySecret(); // Construct signing key
$h1Key = hmacSign($signingKey, $date); // Step 1: Sign the date
$h2Key = hmacSign($h1Key, $region); // Step 2: Sign the region
$h3Key = hmacSign($h2Key, $product); // Step 3: Sign the product
$h4Key = hmacSign($h3Key, "aliyun_v4_request"); // Step 4: Sign the request
// Calculate final signature
$signature = hash_hmac('sha256', $stringToSign, $h4Key);
// Build form data for POST request
$bodyWriter = new CURLFileUpload(); // Create form builder instance
// Add fields to the form
$bodyWriter->addField('key', $key); // Object name
$bodyWriter->addField('policy', $stringToSign); // Base64 encoded Policy
$bodyWriter->addField('x-oss-signature-version', 'OSS4-HMAC-SHA256'); // Signature version
$bodyWriter->addField('x-oss-credential', sprintf("%s/%s/%s/%s/aliyun_v4_request",
$cred->getAccessKeyId(), $date, $region, $product)); // Credential information
$bodyWriter->addField('x-oss-date', $utcTime->format('Ymd\THis\Z')); // Timestamp
$bodyWriter->addField('x-oss-signature', $signature); // Final signature
// Add file content to the form
$bodyWriter->addFileFromString('file', $data); // Upload file content
$postData = $bodyWriter->getFormData(); // Get complete form data
// Send POST request
$client = new \GuzzleHttp\Client(); // Create HTTP client
$response = $client->post(
sprintf("http://%s.oss-%s.aliyuncs.com/", $bucket, $region), // OSS upload URL
[
'headers' => [
'content-type' => $bodyWriter->getContentType(), // Set Content-Type
],
'body' => $postData // Set request body
]
);
// Check response status code
if ($response->getStatusCode() < 200 || $response->getStatusCode() >= 300) {
echo "Post Object Fail, status code:" . $response->getStatusCode() . ", reason: " . $response->getReasonPhrase() . PHP_EOL;
exit(1); // Exit the program if status code is not in 2xx range
}
// Print upload result
echo "post object done, status code:" . $response->getStatusCode() . ", request id:" . $response->getHeaderLine('x-oss-request-id') . PHP_EOL;
/**
* HMAC signature function
* @param string $key Signing key
* @param string $data Data to be signed
* @return string Returns signature result
*/
function hmacSign($key, $data)
{
return hash_hmac('sha256', $data, $key, true); // Generate HMAC signature using SHA256 algorithm
}
/**
* Form builder class for generating multipart/form-data format request body
*/
class CURLFileUpload
{
private $fields = []; // Store regular fields
private $files = []; // Store file fields
private $boundary; // Separator
public function __construct()
{
$this->boundary = uniqid(); // Generate unique separator
}
/**
* Add regular field
* @param string $name Field name
* @param string $value Field value
*/
public function addField($name, $value)
{
$this->fields[$name] = $value;
}
/**
* Add file field
* @param string $name Field name
* @param string $content File content
*/
public function addFileFromString($name, $content)
{
$this->files[$name] = [
'content' => $content,
'filename' => $name,
'type' => 'application/octet-stream' // Default MIME type
];
}
/**
* Get complete form data
* @return string Returns request body in multipart/form-data format
*/
public function getFormData()
{
$data = '';
foreach ($this->fields as $name => $value) {
$data .= "--{$this->boundary}\r\n";
$data .= "Content-Disposition: form-data; name=\"$name\"\r\n\r\n";
$data .= $value . "\r\n";
}
foreach ($this->files as $name => $file) {
$data .= "--{$this->boundary}\r\n";
$data .= "Content-Disposition: form-data; name=\"$name\"; filename=\"{$file['filename']}\"\r\n";
$data .= "Content-Type: {$file['type']}\r\n\r\n";
$data .= $file['content'] . "\r\n";
}
$data .= "--{$this->boundary}--\r\n";
return $data;
}
/**
* Get Content-Type header
* @return string Returns Content-Type value
*/
public function getContentType()
{
return "multipart/form-data; boundary={$this->boundary}";
}
}
References
For a complete example of form upload, see Github example.