简体   繁体   English

PHP:获取文件扩展名不能上传到S3

[英]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. 我使用Amazon S3 API上传文件,每次上传时我都会更改文件名。

So for example: 例如:

Dog.png > 3Sf5f.png 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: 所以我将random_string设置为name参数:

$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 . 因此该文件将上传为3Sf5f而不是3Sf5f.png

The variable $filename gives me the full name of the file with its extension. 变量$ filename为我提供了文件的全名及其扩展名。

If I use $params->key = rand_string(5).'${filename}'; 如果我使用$params->key = rand_string(5).'${filename}'; I get: 我明白了:

3Sf5fDog.png

So I tried to retrieve the $filename extension and apply it. 所以我试图检索$ filename扩展并应用它。 I tried more than 30 methods without any positive one. 我尝试了30多种方法而没有任何积极的方法。

For example I tried the $path_info(), I tried substr(strrchr($file_name,'.'),1); 例如,我尝试了$ path_info(),我尝试了substr(strrchr($ file_name,'。'),1); any many more. 还有更多。 All of them give me either 3Sf5fDog.png or just 3Sf5f . 所有这些都给我3Sf5fDog.png或只是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). 整个类文件: http//pastebin.com/QAwJphmW (整个脚本没有其他文件)。

What I'm I doing wrong? 我做错了什么? This is really frustrating. 这真令人沮丧。

The variable $filename (and thus "${filename}") is NOT IN SCOPE at line 1053 of your code (line numbering based on raw paste from pastebin). 变量$ filename(以及因此“$ {filename}”)在代码的第1053行不在范围内(基于来自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 我认为这是PHP的扩展:在上传之前重命名文件

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. 简单的回答:你不能按照你设想的那样去做。为什么 - 在创建URL时没有解析'$ filename',但是变量被传递到Amazon S3并在那里处理。

The solution 解决方案

So, the only option I can think of is to have use the "successRedirect" parameter to point to another URL. 因此,我能想到的唯一选择是使用“successRedirect”参数指向另一个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 ). 该URL将从Amazon( http://doc.s3.amazonaws.com/proposals/post.html#Dealing_with_Success )接收“bucket”和“key”作为查询参数。 Point that to a PHP script that renames the file on Amazon S3 (copy + delete), then redirects the user to another success screen. 指向在Amazon S3上重命名文件的PHP脚本(复制+删除),然后将用户重定向到另一个成功屏幕。

So, 所以,

in your code, line 34, 在你的代码中,第34行,

  1. add a fully qualified URL to a new php script file you're going to write. 将一个完全限定的URL添加到您要编写的新PHP脚本文件中。
  2. the php script wil get the bucket and key passed to it php脚本将获取桶和密钥传递给它
  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 使用函数“public static function copyObject($ srcBucket,$ srcUri,$ bucket,$ uri)”将上传的文件复制到新名称
  5. then delete the original (using deleteObject($bucket, $uri)) 然后删除原始文件(使用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. 所以这个解决方案(这是没有你转移到中间服务器,重命名和上传的唯一方法)它将上传a的成本从每1000上传1c上传到每1000上传2c的成本加倍。 It's taken me 10 minutes @ $200/hour to find that out and respond = $33 = 1,666,666 uploads! 我花了10分钟@ 200美元/小时来找到它并回复= $ 33 = 1,666,666上传! 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. 与其他解决方案比较:对网络服务器发帖,重命名文件,然后从网络服务器上传:你将所有带宽从clinet直接移动到你自己 - 两次。 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) 对于每次上传,创建一个临时存储桶(如果您担心成本,则每1000个存储桶1c)
  3. upload to temporary bucket 上传到临时存储桶
  4. create random name, check if does not exist in final bucket (that 1c per 1000 checks) 创建随机名称,检查最终存储桶中是否不存在(每1000次检查1c)
  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. 定期清理文件上传未完成的存储桶。
$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. 这个explode()将文件名分成一个数组,其中句点为分隔符,然后抓取数组的最后一部分(包含文件名为foo.bar.jpg),并在其前面放置一个句点。 This should get you the desired extension to append it to the rand_string(5). 这应该为您提供所需的扩展名,以将其附加到rand_string(5)。

$params->key = rand_string(5).$extension;

if you're uploading images try this 如果你正在上传图片试试这个

$dim = getimagesize($file);
$type = $dim[2];
if( $type == IMAGETYPE_JPEG ) $ext=".jpg"; 
if( $type == IMAGETYPE_GIF ) $ext=".gif"; 
if( $type == IMAGETYPE_PNG ) $ext=".png";

$params->key = rand_string(5).$ext;

I think something as simple as below should work to extract file extension from the file-name: 我认为下面这么简单应该可以从文件名中提取文件扩展名:

function getFileExtension($fileName)
{
    $ext = '';
    if(strpos($fileName, ".") !== false)
    {
        $ext = end(explode(".", $fileName));
    }
    return $ext;
}

What happends if you: 如果你发生了什么:

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

Do you get something like 'Dog.png'? 你得到像'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. 如果你确实得到像'Dog.png'这样的东西,这就是我用来获取文件扩展名的内容。

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

Then you should be able to do this: 然后你应该能够做到这一点:

$params->key = rand_string(5).'.'.$extension;

You need to first find out what the original extension is and not rename the entire file. 您需要首先找出原始扩展名是什么,而不是重命名整个文件。 So keep the extension and rename de file name. 因此,请保留扩展名并重命名de文件名。

Assuming you have image name in $image_name: 假设您在$ image_name中有图像名称:

$image_name = "image.png";
$random_string = "random";

list($filename,$fileext) = explode(".",$image_name);
$new_name = $random_string.'.'.$fileext;
rename($image_name,$new_name);  

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. 我做的是,我用javascript提取文件扩展名并通过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. 在下一个php文件中,您可以直接使用$ _POST ['ext']作为扩展名。 hope that helped. 希望有所帮助。 let me know if you have any trouble implementing this 如果您在执行此操作时遇到任何问题,请告诉我们

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. 您的函数生成的文件名太短(5个字符),这样会创建更长的文件名,避免碰撞文件名。

example output: aff5a25e84311485d4eedea7e5f24a4f.png 示例输出:aff5a25e84311485d4eedea7e5f24a4f.png

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). 它实际上正在发生的是现在而不是完全生成文件名,你实际上是通过接口传递一个非常小的'程序',因此它可以在以后产生文件名(当变量$ filename存在并且在范围内时) )。 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. (一旦你开始工作,你可能想重新审视你的命名方案。也许名字需要更长一点。或者它应该包括一些可识别的信息{如今天的日期,或文件的原始名称}。点击$ file_base.rand_string(7)。$ file_extension。

A simple solution to re-name a file and compute the extension : 重命名文件并计算扩展名的简单解决方案

$fileName = 'myRandomFile.jpg';

// separate the '.'-separated parts of the file name
$parts = explode( '.', $fileName );

// Solution will not work, if no extension is present
assert( 1 < count( $parts ) );

// keep the extension and drop the last part
$extension = $parts[ count( $parts ) - 1 ];
unset( $parts[ count( $parts ) - 1 ] );

// finally, form the new file name
$newFileName = md5( 'someSeedHere' + implode( '.', $parts )) . '.' . $extension;

echo $extension             // outputs jpg
   . ' - ' 
   . $newFileName           // outputs cfcd208495d565ef66e7dff9f98764da.jpg
   ;

Note, that md5() is always 32 bytes long and non unique regarding the computed value . 注意, md5()总是32字节长,并且对于计算值不唯一 For for many practical instances, it's unique enough . 对于许多实际情况,它足够独特

Addendum 附录

Additionally, you may use this solution to trace variable changes : 此外,您可以使用此解决方案来跟踪变量更改

abstract class CSTReportDelegate {

    abstract public function emitVariableChange( $variableName, $oldValue, $newValue );
    abstract public function emitVariableSetNew( $variableName, $newValue );

}

class CSTSimpleReportDelegate extends CSTReportDelegate {

    public function emitVariableChange( $variableName, $oldValue, $newValue ) {
        echo '<br />[global/change] '. $variableName . ' : ' . print_r( $oldValue, true ) . ' &rarr; ' . print_r( $newValue, true );
    }

    public function emitVariableSetNew( $variableName, $newValue ) {
        echo '<br />[global/init] '. $variableName . '   &rarr; ' . print_r( $newValue, TRUE );
    }

}


class CSysTracer {

    static protected 
        $reportDelegate;

    static private 
        $globalState = array();

    static private  
        $traceableGlobals = array();

    static private 
        $globalTraceEnabled = FALSE;

    const 
        DEFAULT_TICK_AMOUNT = 1;

    static public 
    function setReportDelegate( CSTReportDelegate $aDelegate ) {
        self::$reportDelegate = $aDelegate;
    }


    static public 
    function start( $tickAmount = self::DEFAULT_TICK_AMOUNT ) {

        register_tick_function ( array( 'CSysTracer', 'handleTick' ) );

    }


    static public 
    function stop() {

        unregister_tick_function( array( 'CSysTracer', 'handleTick' ) );

    }

    static public 
    function evalAndTrace( $someStatement ) {

        declare( ticks = 1 ); {
            self::start();
            eval( $someStatement );
            self::stop();
        }
    }

    static public 
    function addTraceableGlobal( $varName ) {

        if ( is_array( $varName )) {
            foreach( $varName as $singleName ) {
                self::addTraceableGlobal( $singleName ); 
            }
            return;
        }

        self::$traceableGlobals[ $varName ] = $varName;

    }

    static public 
    function removeTraceableGlobal( $varName ) {
        unset( self::$traceableGlobals[ $varName ] );   
    }

    /**
     * Main function called at each tick. Calls those functions, which
     * really perform the checks.
     * 
     */
    static public 
    function handleTick( ) {

        if ( TRUE === self::$globalTraceEnabled ) { 
            self::traceGlobalVariable();
        }

    }

    static public 
    function enableGlobalsTrace() {
        self::$globalTraceEnabled = TRUE;   
    }


    static public 
    function disableGlobalsTrace() {
        self::$globalTraceEnabled = FALSE;  
    }

    static public 
    function traceGlobalVariable( ) {

        foreach( self::$traceableGlobals as $aVarname ) {

            if ( ! isset( $GLOBALS[ $aVarname ] )) {
                continue;
            }

            if ( ! isset( self::$globalState[ $aVarname ] ) ) {

                self::$reportDelegate->emitVariableSetNew( $aVarname, $GLOBALS[ $aVarname ] );
                self::$globalState[ $aVarname ] = $GLOBALS[ $aVarname ];
                continue;
            }

           if ( self::$globalState[ $aVarname ] !== $GLOBALS[ $aVarname ]) {

             self::$reportDelegate->emitVariableChange( $aVarname, self::$globalState[ $aVarname ], $GLOBALS[ $aVarname ] );

           }

           self::$globalState[ $aVarname ] = $GLOBALS[ $aVarname ];

        }

    }

}

A sample use case: 示例用例:

ini_set("display_errors", TRUE);
error_reporting(E_ALL);

require_once( dirname( __FILE__ ) . '/CStatementTracer.inc.php' );

/* Ticks make it easy to have a function called for every line of PHP
 *  code. We can use this to track the state of a variable throughout
 * the execution of a script.
 */



CSysTracer::addTraceableGlobal( array( 'foo', 'bar' ));

CSysTracer::setReportDelegate( new CSTSimpleReportDelegate() ); 
CSysTracer::enableGlobalsTrace();

CSysTracer::start(); 
declare( ticks = 1 );

   //
   // At this point, tracing is enabled. 
   // Add your code or call your functions/methods here
   //

CSysTracer::stop();

Untested, but simple enough to work: 未经测试,但很简单,可以工作:

$ext = pathinfo($filename, PATHINFO_EXTENSION);

will return the extension part (without the '.') 将返回扩展部分(没有'。')

How about this? 这个怎么样?

$temp = rand_string(5).'${filename}';         //should be 3Sf5fDog.png
$ext = pathinfo($temp, PATHINFO_EXTENSION); //should be .png
$temp2 = rand_string(5) . $ext;             //should be 4D47a.png

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM