Upload Pre-Signed Object
If you want to allow others to download the object without providing your authentication keys, you can use the pre-signed upload method.
Components
- Bucket Name
- Object Name
- Object ACL
- URL Validation Period
Object Name (Key)
Object name after uploading to bucket.
- .NET
- PHP
- Python
- Javascript
- GO
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
namespace GenPresignedURLExample
{
using System;
using Amazon;
using Amazon.S3;
using Amazon.S3.Model;
/// <summary>
/// This example generates a presigned URL for an object in an Amazon
/// Simple Storage Service (Amazon S3) bucket. The generated example
/// remains valid for the specified number of hours. This example was
/// created using the AWS SDK for .NET version 3.7 and .NET Core 5.0.
/// </summary>
public class GenPresignedURL
{
private const string OBJECT_NAME = "<BUCKET_NAME>";
private static string LOCAL_PATH = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
public static void Main()
{
const string bucketName = "<OBJECT_NAME>";
var path = $"{LOCAL_PATH}/{OBJECT_NAME}";
var objectKey = path;
// Specify how long the presigned URL lasts, in hours
const double timeoutDuration = 12;
// Specify the AWS Region of your Amazon S3 bucket. An example AWS
// Region is shown.
RegionEndpoint bucketRegion = RegionEndpoint.USWest2;
var awsCredentials = new Amazon.Runtime.BasicAWSCredentials("<ACCESS-KEY>", "<SECRET-KEY>");
var config = new AmazonS3Config { ServiceURL = "<ENDPOINT>" };
IAmazonS3 _s3Client = new AmazonS3Client(awsCredentials, config);
string urlString = GeneratePresignedURL(_s3Client, bucketName, objectKey, timeoutDuration);
Console.WriteLine($"The generated URL is: {urlString}");
}
/// <summary>
/// Gemerate a presigned URL that can be used to access the file named
/// in the ojbectKey parameter for the amount of time specified in the
/// duration parameter.
/// </summary>
/// <param name="client">An initialized S3 client object used to call
/// the GetPresignedUrl method.</param>
/// <param name="bucketName">The name of the S3 bucket containing the
/// object for which to create the presigned URL.</param>
/// <param name="objectKey">The name of the object to access with the
/// presigned URL.</param>
/// <param name="duration">The length of time for which the presigned
/// URL will be valid.</param>
/// <returns>A string representing the generated presigned URL.</returns>
public static string GeneratePresignedURL(IAmazonS3 client, string bucketName, string objectKey, double duration)
{
string urlString = string.Empty;
try
{
GetPreSignedUrlRequest request = new ()
{
BucketName = bucketName,
Key = objectKey,
Expires = DateTime.UtcNow.AddHours(duration),
};
urlString = client.GetPreSignedURL(request);
}
catch (AmazonS3Exception ex)
{
Console.WriteLine($"Error:'{ex.Message}'");
}
return urlString;
}
}
}
<?php
// https://docs.aws.amazon.com/AmazonS3/latest/dev/HTTPPOSTForms.html
require('client.php');
$bucket = $config['sample_bucket'];
// Set some defaults for form input fields
$formInputs = ['acl' => 'public-read'];
// Construct an array of conditions for policy
$options = [
['acl' => 'public-read'],
['bucket' => $bucket],
['starts-with', '$key', 'users/'],
];
// Optional: configure expiration time string
$expires = '+2 hours';
$postObject = new \Aws\S3\PostObjectV4(
$client,
$bucket,
$formInputs,
$options,
$expires
);
// Get attributes to set on an HTML form, e.g., action, method, enctype
$formAttributes = $postObject->getFormAttributes();
// Get form input fields. This will include anything set as a form input in
// the constructor, the provided JSON policy, your AWS access key ID, and an
// auth signature.
$formInputs = $postObject->getFormInputs();
var_dump($formInputs);
First, create a signed upload link using the following snippet:
import json
import logging
import os
import pathlib
import boto3
import requests
from botocore.exceptions import ClientError
logging.basicConfig(level=logging.INFO)
config = {
"endpoint_url": "https://s3.ir-thr-at1.arvanstorage.ir",
"secret_key": "YOUR_SECRET_KEY",
"access_key": "YOUR_ACCESS_KEY"
}
# s3 client instance
s3_client = boto3.client(
's3',
endpoint_url=config['endpoint_url'],
aws_access_key_id=config['access_key'],
aws_secret_access_key=config['secret_key']
)
def create_presigned_post(bucket_name, object_name, fields=None, conditions=None, expiration=3600):
"""Generate a pre-signed URL S3 POST request to upload a file
:param bucket_name: string
:param object_name: string
:param fields: Dictionary of prefilled form fields
:param conditions: List of conditions to include in the policy
:param expiration: Time in seconds for the presigned URL to remain valid
:return: Dictionary with the following keys:
url: URL to post to
fields: Dictionary of form fields and values to submit with the POST
:return: None if error.
"""
# Generate a pre-signed S3 POST URL
try:
response = s3_client.generate_presigned_post(
bucket_name,
object_name,
Fields=fields,
Conditions=conditions,
ExpiresIn=expiration
)
except ClientError as e:
logging.error(e)
return None
# The response contains the presigned URL and required fields
return response
def main():
bucket_name = 'YOUR_BUCKET_NAME'
object_name = 'YOUR_OBJECT_NAME'
response = create_presigned_post(bucket_name, object_name)
logging.info(f"response: {response}")
Expected Output:
INFO:root:response: {'url': 'https://s3.ir-thr-at1.arvanstorage.ir/YOUR_BUCKET_NAME', 'fields': {'key': 'YOUR_OBJECT_NAME', 'AWSAccessKeyId': 'xxxxx-xxxxx-xxxxx-xxxx-xxxxxxx', 'policy': 'xxxxxxxxxx', 'signature': 'xxxxxxxxx-xxxxxxx-xxx'}}
Then you need to enable CORS settings for the POST method on your bucket. You can use the following code snippet for this:
def set_bucket_cors(bucket: str) -> None:
"""
Set CORS configuration for the specified bucket
:param bucket: bucket's name
:return:
"""
# Define the configuration rules
cors_configuration = {
'CORSRules': [{
'AllowedHeaders': ['*'],
'AllowedMethods': ['POST'],
'AllowedOrigins': ['*']
}]
}
s3_client.put_bucket_cors(
Bucket=bucket,
CORSConfiguration=cors_configuration
)
Note that you can change AllowedOrigins settings according to your website's domain.
And finally, using the signed data, upload your files as follows:
<html>
<script>
function onClick(){
var fileVal=document.getElementById("file_to_upload").files[0];
console.log(fileVal)
uploadFile(fileVal)
}
function uploadFile(file) {
var formData = new FormData();
formData.append("key", "YOUR_OBJECT_NAME");
formData.append("AWSAccessKeyId", "************************");
formData.append("policy", "************************");
formData.append("signature", "***********************");
formData.append("file", file);
var xhr = new XMLHttpRequest();
xhr.upload.addEventListener("progress", function (e) {
if (e.lengthComputable) {
console.log(e.loaded / e.total);
}
});
xhr.upload.addEventListener("load", function () {
console.log("uploaded");
});
xhr.open("POST", "https://s3.ir-thr-at1.arvanstorage.ir/YOUR_BUCKET_NAME");
xhr.send(formData);
}
</script>
<body>
<input id="file_to_upload" type="file" name="file" /> <br />
<input type="button" onclick="onClick()" name="submit" value="Upload to Arvan S3" />
</body>
</html>
// Import required AWS SDK clients and commands for Node.js
const { S3Client } = require('@aws-sdk/client-s3');
const { createPresignedPost } = require('@aws-sdk/s3-presigned-post');
const { createReadStream } = require('fs');
const FormData = require('form-data');
// Create an S3 client service object
const s3 = new S3Client({
region: 'default',
endpoint: 'endpoint_url',
forcePathStyle: false,
credentials: {
accessKeyId: 'access_key',
secretAccessKey: 'secret_key',
},
});
const BUCKET_NAME = 'sample_bucket';
const file = __dirname + '/../files/file.png';
const Conditions = [
{ acl: 'public-read' },
{ bucket: BUCKET_NAME },
['starts-with', '$key', 'user/'],
];
const Bucket = BUCKET_NAME;
const Key = 'user/file1.png';
const Fields = {
acl: 'public-read',
};
const run = async () => {
const { url, fields } = await createPresignedPost(s3, {
Bucket,
Key,
Conditions,
Fields,
Expires: 600, //Seconds before the presigned post expires. 3600 by default.
});
const form = new FormData();
Object.entries(fields).forEach(([field, value]) => {
form.append(field, value);
});
form.append('file', createReadStream(file));
form.submit(url, (err, res) => {
console.log(res.statusCode);
//handle the response
});
};
run();
package main
import (
"crypto/md5"
"encoding/base64"
"fmt"
"net/http"
"os"
"strings"
"time"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/credentials"
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/s3"
)
// Creates a 15 min URL to put a file that is exactly like Body
// that defined in PutObjectRequest
//
// Usage:
// go run upload_presigned.go BUCKET FILENAME
func main() {
if len(os.Args) != 3 {
exitErrorf("bucket and file name required\nUsage: %s bucket_name filename",
os.Args[0])
}
bucket := os.Args[1]
filename := os.Args[2]
fileContent, err := os.ReadFile(filename)
if err != nil {
exitErrorf("Unable to open file %q, %v", err)
}
h := md5.New()
content := strings.NewReader(string(fileContent))
content.WriteTo(h)
// Initialize a session in us-west-2 that the SDK will use to load
// credentials from the shared credentials file ~/.aws/credentials.
sess, err := session.NewSession(&aws.Config{
Credentials: credentials.NewStaticCredentials("<ACCESS_KEY>", "<SECRET_KEY>", ""),
})
svc := s3.New(sess, &aws.Config{
Region: aws.String("default"),
Endpoint: aws.String("<ENDPOINT_URL>"),
})
resp, _ := svc.PutObjectRequest(&s3.PutObjectInput{
Bucket: aws.String(bucket),
Key: aws.String(filename),
Body: strings.NewReader(string(fileContent)),
})
md5s := base64.StdEncoding.EncodeToString(h.Sum(nil))
resp.HTTPRequest.Header.Set("Content-MD5", md5s)
url, err := resp.Presign(15 * time.Minute)
if err != nil {
fmt.Println("error presigning request", err)
return
}
fmt.Println(url)
fmt.Println()
req, err := http.NewRequest("PUT", url, strings.NewReader(string(fileContent)))
req.Header.Set("Content-MD5", md5s)
if err != nil {
fmt.Println("error creating request", url)
return
}
defClient, err := http.DefaultClient.Do(req)
fmt.Println(defClient, err)
}
func exitErrorf(msg string, args ...interface{}) {
fmt.Fprintf(os.Stderr, msg+"\n", args...)
os.Exit(1)
}
The following command can be used to execute the aforementioned code, presuming the code file is called upload_presigned.go:
go run upload_presigned.go BUCKET FILENAME
If the Body value is not defined in the PutObjectRequest function and the Hash value is not added to the header of this function, the user can load the object with any value with this URL.