Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

PHP: Get file extension not working on uploads to S3

I am using the Amazon S3 API to upload files and I am changing the name of the file each time I upload.

So for example:

Dog.png > 3Sf5f.png

Now I got the random part working as such:

function rand_string( $length ) {
            $chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";  

            $size = strlen( $chars );
            for( $i = 0; $i < $length; $i++ ) {
                $str .= $chars[ rand( 0, $size - 1 ) ];
            }

            return $str;
        }   

So I set the random_string to the name parameter as such:

$params->key = rand_string(5);

Now my problem is that this wont show any extension. So the file will upload as 3Sf5f instead of 3Sf5f.png.

The variable $filename gives me the full name of the file with its extension.

If I use $params->key = rand_string(5).'${filename}'; I get:

3Sf5fDog.png

So I tried to retrieve the $filename extension and apply it. I tried more than 30 methods without any positive one.

For example I tried the $path_info(), I tried substr(strrchr($file_name,'.'),1); any many more. All of them give me either 3Sf5fDog.png or just 3Sf5f.

An example of what I tried:

// As @jcinacio pointed out. Change this to: 
//
//   $file_name = "${filename}";
//
$file_name = '${filename}'  // Is is wrong, since '..' does not evaluate 

$params->key = rand_string(5).$file_name;
=
3Sf5fDog.png

.

$file_name = substr(strrchr('${filename}', '.'), 1);

$params->key = rand_string(5).$file_name;
=
3Sf5f

.

$filename = "example.png"   // If I declare my own the filename it works.
$file_name = substr(strrchr('${filename}', '.'), 1);

$params->key = rand_string(5).$file_name;
=
3Sf5f.png

The entire class file: http://pastebin.com/QAwJphmW (there are no other files for the entire script).

What I'm I doing wrong? This is really frustrating.

like image 683
jQuerybeast Avatar asked May 13 '12 19:05

jQuerybeast


People also ask

Does PHP work in S3?

S3 doesn't run any sort of CGI script (PHP, Perl, Ruby, etc). Think of it as a static html and image repository. If you want to host your PHP application on AWS, consider using AWS Beanstalk.

What file formats does S3 support?

Valid formats are CSV , TSV , CLF , ELF , and JSON . The default value is CSV . delimiter – (Optional) Specify the file field delimiter.

How do I view uploaded files on AWS S3?

In AWS Explorer, expand the Amazon S3 node, and double-click a bucket or open the context (right-click) menu for the bucket and choose Browse. In the Browse view of your bucket, choose Upload File or Upload Folder. In the File-Open dialog box, navigate to the files to upload, choose them, and then choose Open.


2 Answers

The variable $filename (and thus "${filename}") is NOT IN SCOPE at line 1053 of your code (line numbering based on raw paste from pastebin).

So, no matter what you do, you'll never find the extension of a variable that does not exist.


And I've finally worked out what you're doing. I presume this is an extension of PHP: Rename file before upload

Simple answer: you can't do it as you envisage.Why - the '$filename' is not parsed at the time that URL is created, but the variable is passed to Amazon S3 and handled there.

The solution

So, the only option I can think of is to have use the "successRedirect" parameter to point to another URL. That URL will receive the "bucket" and "key" as query parameters from Amazon (http://doc.s3.amazonaws.com/proposals/post.html#Dealing_with_Success). Point that to a PHP script that renames the file on Amazon S3 (copy + delete), then redirects the user to another success screen.

So,

in your code, line 34,

  1. add a fully qualified URL to a new php script file you're going to write.
  2. the php script wil get the bucket and key passed to it
  3. Create the new filename from the "key"
  4. use the function "public static function copyObject($srcBucket, $srcUri, $bucket, $uri)" to copy the uploaded file to the new name
  5. then delete the original (using deleteObject($bucket, $uri))
  6. then redirect the user to where you want to send them

That will do exactly what you want.


In response to your comments "Is this the only way - what about the costs as Amazon charge per request?"

Delete requests are free. No data transfer costs when moving on the same bucket (or even in the same region). So this solution (which is the only way without you transferring to an intermediate server, renaming and uploading) it doubles the cost of upload a from 1c per 1000 uploads to 2c per 1000 uploads. It's taken me 10 minutes @ $200/hour to find that out and respond = $33 = 1,666,666 uploads! Costs pale a bit when you do the maths :)

Compare with the other solution: do a post to an webserver, rename the file and then upload from the webserver: you move all the bandwidth from the clinet tdirectly to yourself - twice. And this also introduces risk and increased possible failure points.


In response to "Doesn't work. I you upload a file then the old one gets deleted"

I would assusme this is not a problem as you upload a file and then rename it within a second or two. But if you want ot gurantee each file gets uploaded, then you need to do a lot more than create a random filename anyway:

  1. have your "final" bucket
  2. for each upload, create a temporary bucket (that's 1c per 1000 buckets, if you're worried on costs)
  3. upload to temporary bucket
  4. create random name, check if does not exist in final bucket (that 1c per 1000 checks)
  5. copy file to final bucket (with new name)
  6. delete uploaded file as well as the bucket.
  7. periodically clean up buckets where the file uploads were not complete.
like image 195
Robbie Avatar answered Sep 28 '22 04:09

Robbie


$fileSplit = explode('.',$filename);
$extension = '.'.$fileSplit[count($fileSplit) - 1];

This explode() divides up the file name into an array with periods as delimiters, and then grabs the last piece of the array (incase a file name is foo.bar.jpg), and puts a period in front of it. This should get you the desired extension to append it to the rand_string(5).

$params->key = rand_string(5).$extension;
like image 33
Jake M Avatar answered Sep 28 '22 04:09

Jake M