How to Upload and Download S3 Files using Plain PHP (No SDK Required)

4 min bacaan

I am familiar with typical cloud storage services like Google Drive and Microsoft OneDrive. However, in web development, these cloud storage providers are not quite suitable because they lack direct, permanent links to the uploaded files. To address this issue, object storage becomes a handy and reliable solution.

In OneDrive, the link provided leads to a web interface rather than the raw file itself. We need a hotlinking feature to use files effectively on the web.

In this article, I will be using Object Storage provided by IPServerOne instead of Amazon S3. The process is almost identical; you simply need to change the $s3_endpoint variable accordingly.

Protip: You can ask ChatGPT/Copilot to reverse-engineer the PHP code into a cURL command if you aren’t familiar with PHP.


Upload to S3

To upload a file to an object storage provider, you just need to pass the correct parameters and execute a cURL request. It is actually very simple and straightforward—much like making a typical REST API call.

Key Logic: The core of the upload logic uses curl_init to open a connection to the S3 endpoint, followed by CURLOPT_PUT to stream the file content directly. This approach is highly efficient as it avoids the overhead of large third-party libraries.

<?php

$file_to_upload = 'yourimage.png';
$bucket = 'wordpressha';
$s3_endpoint = 'ap-southeast-mys1.oss.ips1cloud.com';
$access_key = '5IRK7V7TVSV0B829WHAD';
$secret_key = 'I7BLtOZAsHdCDHMeyJ73QipBIPjLDCvWhIAzfynV';

$content_type = 'application/octet-stream';
$date_value = gmdate('D, d M Y H:i:s T');

$string_to_sign = "PUT\n\n{$content_type}\n{$date_value}\n/{$bucket}/{$file_to_upload}";
$signature = base64_encode(hash_hmac('sha1', $string_to_sign, $secret_key, true));

$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, "https://{$bucket}.{$s3_endpoint}/{$file_to_upload}");
curl_setopt($ch, CURLOPT_PUT, true);
curl_setopt($ch, CURLOPT_INFILE, fopen($file_to_upload, 'r'));
curl_setopt($ch, CURLOPT_INFILESIZE, filesize($file_to_upload));
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    "Host: {$bucket}.{$s3_endpoint}",
    "Date: {$date_value}",
    "Content-Type: {$content_type}",
    "Authorization: AWS {$access_key}:{$signature}"
]);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);

$response = curl_exec($ch);
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);

if ($http_code == 200) {
    echo "File uploaded successfully.";
} else {
    echo "Failed to upload file. HTTP Status Code: {$http_code}";
}

Demo 💻

This demo shows how a file is uploaded directly to Object Storage:

Simple and straightforward. You don\’t have to import any heavy libraries just to upload files to S3.

Accessing Private Bucket Files (Download from S3)

Objects (files) stored in a public bucket can be accessed directly via their URL. However, for a private bucket, the URL requires additional authentication parameters in the query string to grant access.

Key Logic: Generating a Presigned URL works by hashing the expiration time and bucket path using hash_hmac with your Secret Key. Since this is a client-side calculation, no API call is required for this step, making it extremely fast.

<?php

$object_key = 'yourimage.png';
$bucket = 'wordpressha';
$s3_endpoint = 'ap-southeast-mys1.oss.ips1cloud.com';
$access_key = '5IRK7V7TVSV0B829WHAD';
$secret_key = 'I7BLtOZAsHdCDHMeyJ73QipBIPjLDCvWhIAzfynV';

$expires = time() + 3600;
$method = 'GET';

$string_to_sign = "{$method}\n\n\n{$expires}\n/{$bucket}/{$object_key}";
$signature = base64_encode(hash_hmac('sha1', $string_to_sign, $secret_key, true));

$presigned_url = "https://{$bucket}.{$s3_endpoint}/{$object_key}?AWSAccessKeyId={$access_key}&Expires={$expires}&Signature=" . urlencode($signature);

echo "Presigned URL: {$presigned_url}\n";

Example access to an object with a Public Bucket

https://ap-southeast-mys1.oss.ips1cloud.com/wordpressha/test.jpg

Example access to an object with a Private Bucket

https://wordpressha.ap-southeast-mys1.oss.ips1cloud.com/test.jpg?AWSAccessKeyId=KL6FSI64D6FEBHY77621&Expires=1734687785&Signature=4zRuNtWz%2F9ewHT%2FUF7c2mCfOxco%3D

Notice the parameters AWSAccessKeyId, Expires, and Signature appended to the URL. This is known as a Presigned URL.

Reviewing the code, you\’ll notice that Presigned URL generation doesn\’t involve an API call. It is calculated purely based on a hash generated from strings salted with your Secret Key.

Demo 💻

This demo illustrates how files in a private bucket can be accessed:


Don\’t worry about attempting to access the test bucket—access has already been revoked. These examples were created solely for the purpose of this article 😉.

By now, you should have a solid understanding of how Object Storage works and how to integrate it with plain PHP! 😇