9

我正在使用 Amazon S3 API 上传文件,并且每次上传时都会更改文件的名称。

例如:

狗.png > 3Sf5f.png

现在我得到了这样的随机部分:

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

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

            return $str;
        }   

所以我将 random_string 设置为 name 参数,如下所示:

$params->key = rand_string(5);

现在我的问题是这不会显示任何扩展名。所以文件将上传为3Sf5f而不是3Sf5f.png.

变量 $filename 为我提供了文件的全名及其扩展名。

如果我使用$params->key = rand_string(5).'${filename}';我得到:

3Sf5fDog.png

所以我试图检索 $filename 扩展名并应用它。我尝试了 30 多种方法,但没有任何积极的方法。

例如我尝试了 $path_info(),我尝试了 substr(strrchr($file_name,'.'),1); 还有更多。他们都给我一个3Sf5fDog.png或一个3Sf5f

我尝试过的一个例子:

// 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

整个类文件: http: //pastebin.com/QAwJphmW(整个脚本没有其他文件)。

我做错了什么?这真是令人沮丧。

4

12 回答 12

8

变量 $filename(因此“${filename}”)不在代码第 1053 行的范围内(行号基于 pastebin 中的原始粘贴)。

所以,无论你做什么,你永远不会找到一个不存在的变量的扩展。


我终于弄清楚了你在做什么。我认为这是PHP 的扩展:上传前重命名文件

简单的答案:你不能按照你的设想去做。为什么 - 在创建 URL 时没有解析“$filename”,但变量被传递给 Amazon S3 并在那里处理。

解决方案

所以,我能想到的唯一选择是使用“successRedirect”参数指向另一个 URL。该 URL 将从 Amazon ( http://doc.s3.amazonaws.com/proposals/post.html#Dealing_with_Success ) 接收“bucket”和“key”作为查询参数。将其指向一个 PHP 脚本,该脚本重命名 Amazon S3 上的文件(复制 + 删除),然后将用户重定向到另一个成功屏幕。

所以,

在您的代码中,第 34 行,

  1. 将完全限定的 URL 添加到您要编写的新 php 脚本文件中。
  2. php 脚本将获取传递给它的存储桶和密钥
  3. 从“密钥”创建新文件名
  4. 使用函数“public static function copyObject($srcBucket, $srcUri, $bucket, $uri)”将上传的文件复制到新名称
  5. 然后删除原始文件(使用 deleteObject($bucket, $uri))
  6. 然后将用户重定向到您要发送的位置

这将完全符合您的要求。


回应您的评论“这是唯一的方法吗?亚马逊对每个请求收取的费用如何?”

删除请求是免费的。在同一个桶(甚至在同一个区域)上移动时没有数据传输成本。因此,此解决方案(这是您无需转移到中间服务器、重命名和上传的唯一方法)它将上传成本从每 1000 次上传 1c 增加到每 1000 次上传 2c 的成本加倍。我花了 10 分钟 @ 200 美元/小时才发现并回复 = 33 美元 = 1,666,666 次上传!当你做数学时,成本有点苍白:)

与其他解决方案相比:向网络服务器发布帖子,重命名文件,然后从网络服务器上传:您将所有带宽从客户端直接转移到您自己 - 两次。这也带来了风险并增加了可能的故障点。


回应“不起作用。我上传文件然后旧文件被删除”

我认为这不是问题,因为您上传文件然后在一两秒钟内重命名它。但是,如果您想保证每个文件都被上传,那么您需要做的不仅仅是创建一个随机文件名:

  1. 拥有你的“最终”桶
  2. 对于每次上传,创建一个临时存储桶(如果您担心成本,则每 1000 个存储桶 1c)
  3. 上传到临时存储桶
  4. 创建随机名称,检查最终存储桶中是否不存在(每 1000 次检查 1c)
  5. 将文件复制到最终存储桶(使用新名称)
  6. 删除上传的文件以及存储桶。
  7. 定期清理文件上传未完成的存储桶。
于 2012-05-19T03:02:45.547 回答
4
$fileSplit = explode('.',$filename);
$extension = '.'.$fileSplit[count($fileSplit) - 1];

这个explode() 将文件名分成一个以句点作为分隔符的数组,然后抓取数组的最后一部分(如果文件名是foo.bar.jpg),并在其前面放置一个句点。这应该会为您提供所需的扩展名,以将其附加到 rand_string(5)。

$params->key = rand_string(5).$extension;
于 2012-05-13T19:43:42.190 回答
2

如果你上传图片试试这个

$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;
于 2012-05-20T14:51:34.823 回答
2

我认为像下面这样简单的东西应该可以从文件名中提取文件扩展名:

function getFileExtension($fileName)
{
    $ext = '';
    if(strpos($fileName, ".") !== false)
    {
        $ext = end(explode(".", $fileName));
    }
    return $ext;
}
于 2012-05-21T07:58:17.240 回答
1

看起来实际发生的事情不是现在完全生成文件名,实际上您通过接口传递了一个非常小的“程序”,因此它可以稍后生成文件名(当变量 $filename 存在并且在范围内时)。界面的另一端最终会执行您传入的“程序”,从而生成修改后的文件名。(当然,将“程序”传递给其他东西以便稍后执行并不会使调试变得真正容易:-)

(当然,您是要“使这项工作”还是“以不同的方式进行”取决于您。“不同的方式”通常涉及在您尝试调用上传界面之前自己重命名或复制文件,并进行了描述在其他答案中。)

如果您决定要“让它工作”,那么整个文件名参数需要是一个程序,而不仅仅是它的一部分。这种有些不常见的功能通常涉及将整个字符串括在引号中。(您还需要对字符串中现有的单引号做一些事情,这样它们就不会过早终止引用的字符串。一种方法是用反斜杠引用它们中的每一个。另一种看起来更清晰且通常有效的方法是用双引号替换它们。)换句话说,我相信下面的代码对你有用(我没有合适的环境来测试它,所以我不确定)。

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

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

(一旦你得到它的工作,你可能想重新审视你的命名方案。也许名称需要更长一点。或者它可能应该包含一些可识别的信息{如今天的日期,或文件的原始名称}。你可以碰到类似 $file_base.rand_string(7).$file_extension 的东西。

于 2012-05-22T21:53:27.747 回答
1

好的,这是我在服务器端获取扩展时遇到问题时使用的另一种尝试。我所做的是,我使用 javascript 提取文件扩展名并通过邮寄方式发送。

<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>

在下一个 php 文件中,您可以直接使用 $_POST['ext'] 作为扩展名。希望有帮助。如果您在执行此操作时遇到任何问题,请告诉我

于 2012-05-21T19:37:47.570 回答
1

我在我的网站中使用它(并且工作多年):

$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); 

您的函数生成的文件名太短(5 个字符),这样会创建更长的文件名,避免文件名冲突。

示例输出:aff5a25e84311485d4eedea7e5f24a4f.png

于 2012-05-22T06:35:40.337 回答
1

您需要首先找出原始扩展名是什么,而不是重命名整个文件。所以保留扩展名并重命名文件名。

假设您在 $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);  
于 2012-05-20T14:46:21.747 回答
1

如果您:

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

你得到像“Dog.png”这样的东西吗?如果您没有获取文件名的方式有问题。如果你确实得到了类似“Dog.png”的东西,这里就是我用来获取文件扩展名的东西。

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

然后你应该能够做到这一点:

$params->key = rand_string(5).'.'.$extension;
于 2012-05-17T19:39:54.327 回答
0

重命名文件并计算扩展名的简单解决方案:

$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
   ;

请注意,md5()始终为 32 字节长,并且对于计算值而言是非唯一的。对于许多实际情况,它已经足够独特了

附录

此外,您可以使用此解决方案来跟踪变量更改

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 ];

        }

    }

}

一个示例用例:

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();
于 2012-05-13T19:45:30.300 回答
0

未经测试,但足够简单,可以工作:

$ext = pathinfo($filename, PATHINFO_EXTENSION);

将返回扩展部分(不带“。”)

于 2012-05-13T19:47:44.970 回答
0

这个怎么样?

$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
于 2012-05-19T20:40:10.177 回答