I am developing a Web Application using PHP. In my application, I need to upload the file to the AWS S3 bucket using Presigned URL. Now, I can read the private file from the S3 bucket using pre-signed like this.
$s3Client = new S3Client([
'version' => 'latest',
'region' => env('AWS_REGION', ''),
'credentials' => [
'key' => env('AWS_IAM_KEY', ''),
'secret' => env('AWS_IAM_SECRET', '')
]
]);
//GetObject
$cmd = $s3Client->getCommand('GetObject', [
'Bucket' => env('AWS_BUCKET',''),
'Key' => 'this-is-uploaded-using-presigned-url.png'
]);
$request = $s3Client->createPresignedRequest($cmd, '+20 minutes');
//This is for reading the image. It is working.
$presignedUrl = (string) $request->getUri();
When I access the $presignedUrl from the browser, I can get the file from the s3. It is working. But now, I am uploading a file to S3. Not reading the file from s3. Normally, I can upload the file to the S3 like this.
$client->putObject(array(
'Bucket' => $bucket,
'Key' => 'data.txt',
'Body' => 'Hello!'
));
The above code is not using the pre-signed URL. But I need to upload the file using a pre-signed URL. How, can I upload the file using a pre-signed URL. For example, what I am thinking is something like this.
$client->putObject(array(
'presigned-url' => 'url'
'Bucket' => $bucket,
'Key' => 'data.txt',
'Body' => 'Hello!'
));
How can I upload?
It seems reasonable that you can create a pre-signed PutPobject command by running:
$cmd = $s3Client->getCommand('PutObject', [
'Bucket' => $bucket,
'Key' => $key
]);
$request = $s3Client->createPresignedRequest($cmd, '+20 minutes')->withMethod('PUT');
Then you might want to perform the PUT call from PHP using:
file_put_contents(
$request->getUri(),
'Hello!',
stream_context_create(['http' => [ 'method' => 'PUT' ]])
);
If you want to create a URL that a browser can submit, then you need to have the browser send the file as a form POST. This AWS documentation explains how to create a pre-signed POST request with the fields that you then need to put into an HTML form and display to the user: https://docs.aws.amazon.com/sdk-for-php/v3/developer-guide/s3-presigned-post.html
Also, this answer might be useful: https://stackoverflow.com/a/59644117/53538
Related
I try to upload an image to s3 using Laravel but I receive a runtime error. Using Laravel 5.8, PHP7 and API REST with Postman I send by body base64
I receive an image base64 and I must to upload to s3 and get the request URL.
public function store(Request $request)
{
$s3Client = new S3Client([
'region' => 'us-east-2',
'version' => 'latest',
'credentials' => [
'key' => $key,
'secret' => $secret
]
]);
$base64_str = substr($input['base64'], strpos($input['base64'], ",") + 1);
$image = base64_decode($base64_str);
$result = $s3Client->putObject([
'Bucket' => 's3-galgun',
'Key' => 'saraza.jpg',
'SourceFile' => $image
]);
return $this->sendResponse($result['ObjectURL'], 'message.', 'ObjectURL');
}
Says:
RuntimeException: Unable to open u�Z�f�{��zڱ��� .......
The SourceFile parameter is leading to the path of file to upload to S3, not the binary
You can use Body parameter to replace the SourceFile, or saving the file to local temporary and get the path for SourceFile
Like this:
public function store(Request $request)
{
$s3Client = new S3Client([
'region' => 'us-east-2',
'version' => 'latest',
'credentials' => [
'key' => $key,
'secret' => $secret
]
]);
$base64_str = substr($input['base64'], strpos($input['base64'], ",") + 1);
$image = base64_decode($base64_str);
Storage::disk('local')->put("/temp/saraza.jpg", $image);
$result = $s3Client->putObject([
'Bucket' => 's3-galgun',
'Key' => 'saraza.jpg',
'SourceFile' => Storage::disk('local')->path('/temp/saraza.jpg')
]);
Storage::delete('/temp/saraza.jpg');
return $this->sendResponse($result['ObjectURL'], 'message.', 'ObjectURL');
}
And, if you're using S3 with Laravel, you should consider the S3 filesystem driver instead of access the S3Client manually in your controller
To do this, add the S3 driver composer require league/flysystem-aws-s3-v3, put your S3 IAM settings in .env or config\filesystems.php
Then update the default filesystem in config\filesystems, or indicate the disk driver when using the Storage Storage::disk('s3')
Detail see document here
Instead of SourceFile you have to use Body. SourceFile is a path to a file, but you do not have a file, you have a base64 encoded source of img. That is why you need to use Body which can be a string. More here: https://docs.aws.amazon.com/aws-sdk-php/v3/api/api-s3-2006-03-01.html#putobject
Fixed version:
public function store(Request $request)
{
$s3Client = new S3Client([
'region' => 'us-east-2',
'version' => 'latest',
'credentials' => [
'key' => $key,
'secret' => $secret
]
]);
$base64_str = substr($input['base64'], strpos($input['base64'], ",") + 1);
$image = base64_decode($base64_str);
$result = $s3Client->putObject([
'Bucket' => 's3-galgun',
'Key' => 'saraza.jpg',
'Body' => $image
]);
return $this->sendResponse($result['ObjectURL'], 'message.', 'ObjectURL');
}
A very simple way to uploads Any file in AWS-S3 Storage.
First, check your ENV setting.
AWS_ACCESS_KEY_ID=your key
AWS_SECRET_ACCESS_KEY= your access key
AWS_DEFAULT_REGION=ap-south-1
AWS_BUCKET=your bucket name
AWS_URL=Your URL
The second FileStorage.php
's3' => [
'driver' => 's3',
'key' => env('AWS_ACCESS_KEY_ID'),
'secret' => env('AWS_SECRET_ACCESS_KEY'),
'region' => env('AWS_DEFAULT_REGION'),
'bucket' => env('AWS_BUCKET'),
'url' => env('AWS_URL'),
//'visibility' => 'public', // do not use this line for security purpose. try to make bucket private.
],
Now come on main Code.
Upload Binary File from HTML Form.
$fileName = 'sh_'.mt_rand(11111,9999).".".$imageFile->clientExtension();;
$s3path = "/uploads/".$this::$SchoolCode."/";
Storage::disk('s3')->put($s3path, file_get_contents($req->file('userDoc')));
Upload Base64 File
For Public Bucket or if you want to keep file Public
$binary_data = base64_decode($file);
Storage::disk('s3')->put($s3Path, $binary_data, 'public');
For Private Bucket or if you want to keep file Private
$binary_data = base64_decode($file);
Storage::disk('s3')->put($s3Path, $binary_data);
I Recommend you keep your file private... that is a more secure way and safe. for this, you have to use PreSign in URL to access that file.
For Pre sign-In URL check this post. How access image in s3 bucket using pre-signed url
Using "aws/aws-sdk-php": "^3.0#dev"
I am creating a image sharing website but do not want people to copy my URLs to another site to steal my content/bandwidth.
I was originally storing the objects as
return $s3->putObject([
'Bucket' => $bucket,
'Key' => $key,
'Body' => $file,
'ACL' => 'public-read',
]);
But I have removed 'public-read' so now the URL below no longer works
https://mybucket-images.s3.us-west-1.amazonaws.com/' . $key);
What do I need to do to create a temporary URL that can still be client side cached to access the object?
One thing I was thinking was to change the key once a week or month, but it would require me to update all objects with a cronjob. There must be a way to create a temporary access URL?
Use your server to generate presigned url for the keys in the bucket.
//Creating a presigned request
$s3Client = new Aws\S3\S3Client([
'profile' => 'default',
'region' => 'us-east-2',
'version' => '2006-03-01',
]);
$cmd = $s3Client->getCommand('GetObject', [
'Bucket' => 'my-bucket',
'Key' => 'testKey'
]);
$request = $s3Client->createPresignedRequest($cmd, '+20 minutes');
$presignedUrl = (string) $request->getUri();
taken from https://docs.aws.amazon.com/sdk-for-php/v3/developer-guide/s3-presigned-url.html
But you'd have to do this every time there's a request to your page. And the link will be valid everywhere. You just minimize the period of its validity.
If your website is an API based and you retrieve the url via API, this may be relevant to you:
If your website has a login function, you can check for the auth logic prior giving the presigned url.
If not, you can use Http Referrer (which can be spoofed). Or an api key (like in API Gateway)
You can use the following code:
// initiate connection to your S3 bucket
$client = new S3Client(['credentials' => ['key' => 's3 key', 'secret' =>'s3 secrete'], 'region' => 's3 region', 'version' => 'latest']);
$object = $client->getCommand('GetObject', [
'Bucket' => 's3 bucket',
'Key' => 'images/image.png' // file
]);
$presignedRequest = $client->createPresignedRequest($object, '+20 minutes');
$presignedUrl = (string)$presignedRequest->getUri();
if ($presignedUrl) {
return $presignedUrl;//presigned URL
} else {
throw new FileNotFoundException();
}
If your intent is to make your content readable ONLY via a URL posted on your website - versus having the same web client using the same url accessed from another site NOT work, I think you are likely to find that rather difficult. Most of the ways that come to mind are fairly spoofable.
I would take a look at this and see if its good enough for you:
Restricting Access to a Specific HTTP Referrer
I'm currently struggling with getting a S3 download link working. I've been using this code as reference but when I try to open the file, I get the error:
The authorization mechanism you have provided is not supported. Please
use AWS4-HMAC-SHA256.
I tried a few other scripts floating around, but all ended with some other error message.
Is there an easy way to migrate the script I'm using to make it work with Signature v4?
UPDATE: as suggested by hjpotter92, I used the AWS-SDK and came up with this working code:
$client = S3Client::factory([
'version' => 'latest',
'region' => 'eu-central-1',
'signature' => 'v4',
'credentials' => [
'key' => '12345',
'secret' => 'ABCDE'
]
]);
$cmd = $client->getCommand('GetObject', [
'Bucket' => '###name###',
'Key' => $fileName
]);
$request = $client->createPresignedRequest($cmd, '+2 minutes');
$presignedUrl = (string) $request->getUri();
return $presignedUrl;
The question it self explanatory, when trying to create a presigned url I get the following error:
Error retrieving credentials from the instance profile metadata server. (cURL error 28: Connection timed out after 1001 milliseconds (see http://curl.haxx.se/libcurl/c/libcurl-errors.html))
I have used the code from here exactly https://docs.aws.amazon.com/aws-sdk-php/v3/guide/service/s3-presigned-url.html
My code is below:
$s3Client = new S3Client([
'region' => 'eu-west-1',
'version' => '2006-03-01',
]);
$cmd = $s3Client->getCommand('GetObject', [
'Bucket' => 'my-bucket-name',
'Key' => 'AKIAJNCZ5***********'
]);
$request = $s3Client->createPresignedRequest($cmd, '+20 minutes');
// Get the actual presigned-url
$presignedUrl = (string) $request->getUri();
print_r($presignedUrl);
Any reason why this is happening?
EDIT::
Ok so this fixed my problem, but it wasnt not actually even in the docs:
$s3Client = new S3Client([
'region' => 'eu-west-1',
'version' => '2006-03-01',
'credentials' => ['key' => 'AKIAJNCZ5MY*******8','secret'=>'NgeFc+2/Q2cUAmL/+lP2gp***********8']
]);
Adding the credentials assoc array :)
However I am now unsure how to use this presigned url to download one of my files aha, so if anyone knows and doesnt mind putting me in the right direction :)
'Key' in the getCommand array is the name/path to the file you want to generate a pre-signed URL for, not your AWS key :)
$cmd = $s3Client->getCommand('GetObject', [
'Bucket' => 'my-bucket-name',
'Key' => 'path/to/file.txt', // or just file.txt if it's in the root of the bucket
]);
I have this code :
require '/home/ubuntu/vendor/autoload.php';
$sharedConfig = [
'region' => 'us-west-2',
'version' => 'latest'
];
$sdk = new Aws\Sdk($sharedConfig);
$s3Client = $sdk->createS3();
$result = $s3Client->putObject([
'Bucket' => 'my-bucket',
'Key' => $_FILES["fileToUpload"]["name"],
'Body' => $_FILES["fileToUpload"]["tmp_name"]
]);
It works, basically. It sends a file to S3. But it apparently sends it badly since it always shows as a corrupted file... Can anyone tell me what I am doing wrong?
To be specific - the image I am uploading is a jpg image and when I try to look at it on the S3 instance, I am told that it "cannot be displayed because it contains errors"