PHP: Get file extension not working on uploads to S3

后端 未结 12 637
我寻月下人不归
我寻月下人不归 2020-12-06 17:49

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

相关标签:
12条回答
  • 2020-12-06 18:29

    i am using this in my websites (and works fine for years):

    $file = 'yourOriginalfile.png';
    
    //get the file extension
    $fileExt = substr(strrchr($file, '.'), 1);
    
    //create a random name and add the original extension
    $fileUniqueName = md5(uniqid(mktime())) . '.' . $fileExt;
    
    rename($file,$fileUniqueName); 
    

    your function generates too short filenames (5 characters), this way creates longer filenames, avoiding to collide the file names.

    example output: aff5a25e84311485d4eedea7e5f24a4f.png

    0 讨论(0)
  • 2020-12-06 18:30

    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.
    0 讨论(0)
  • 2020-12-06 18:30

    It appears what's actually going on is rather than fully producing the filename right now, you're in effect passing a very small 'program' through the interface so it can then produce the filename later (when the variable $filename exists and is in scope). The other side of the interface eventually executes that 'program' you pass in, which produces the modified filename. (Of course passing a 'program' to something else to execute later doesn't tend to make debugging real easy:-)

    (It's of course up to you whether you want to "make this work" or "do it a different way". "Different ways" typically involve renaming or copying the file yourself before you even try to invoke the upload interface, and are described in other answers.)

    If you decide you want to "make it work", then the entire filename parameter needs to be a program, rather than just part of it. This somewhat uncommon functionality typically involves enclosing the entire string in single quotes. (You also need to do something about existing single quote marks inside the string so they don't terminate the quoted string too soon. One way is to quote each of them with a backslash. Another way that may look cleaner and usually works is to replace them with double quotes.) In other words, I believe the code below will work for you (I haven't got the right environment to test it, so I'm not sure).

    $file_extension = 'substr(strrchr(${filename}, "."), 1)';
    
    $params->key = rand_string(5).$file_extension;
    

    (Once you get it working, you might want to revisit your naming scheme. Perhaps the name needs to be a little longer. Or perhaps it should include some identifiable information {like today's date, or the original name of the file}. You may hit on something like $file_base.rand_string(7).$file_extension.

    0 讨论(0)
  • 2020-12-06 18:34

    What happends if you:

    $filename = "${filename}";
    echo $filename;
    die();
    

    Do you get something like 'Dog.png'? If you don't there is something wrong in the way you are getting the filename. If you do get something like 'Dog.png', here is what I use to get the file extension.

    $pieces = explode('.',trim($filename));
    $extension = end($pieces);
    

    Then you should be able to do this:

    $params->key = rand_string(5).'.'.$extension;
    
    0 讨论(0)
  • 2020-12-06 18:36

    ok here's another try that I used when I had trouble getting the extension on the server side. what I did was, I used javascript to extract the file extension and the send it via post.

    <script type="text/javascript" >
    function fileinfo() {
    var file = document.form.file.value;
    document.getElementById("ext").value = file.split('.').pop();
    document.myform.submit();   
    }
    </script>
    <form name="myform" enctype="multipart/form-data" onsubmit="fileinfo();">
    <input type="file" name="file">
    <input type="hidden" name="ext">
    //rest of the form
    </form>
    

    in the next php file you can directly use $_POST['ext'] as extension. hope that helped. let me know if you have any trouble implementing this

    0 讨论(0)
  • 2020-12-06 18:37
    $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;
    
    0 讨论(0)
提交回复
热议问题