【问题标题】:PHP: Get file extension not working on uploads to S3PHP:获取文件扩展名不适用于上传到 S3
【发布时间】:2012-05-21 10:08:59
【问题描述】:

我正在使用 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-&gt;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(整个脚本没有其他文件)。

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

【问题讨论】:

  • 是否有可能提供可编译的实际代码?所有示例都有错误...
  • 实际代码是什么意思?整个文件?
  • 例如,您的样本缺少必需的分号。
  • 我在打电话。我期待着缺少一些东西。对不起。在我的实际代码中没有“编译”错误。
  • 注意:$filename = "${file_name}";$filename = '${file_name}'; 完全不同,因为第二个不将 $file_name 评估为变量!

标签: php file-upload amazon-s3 file-extension


【解决方案1】:

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

因此,无论您做什么,都永远找不到不存在的变量的扩展名。


我终于弄清楚了你在做什么。我想这是PHP: Rename file before upload的扩展

简单的答案:你不能按照你的设想去做。为什么 - 在创建 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. 从“key”创建新文件名
  4. 使用函数“公共静态函数” copyObject($srcBucket, $srcUri, $bucket, $uri)" 复制上传的 文件改名
  5. 然后删除原来的(使用deleteObject($bucket, $uri))
  6. 然后将用户重定向到您要发送的位置

这正是你想要的。


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

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

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


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

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

  1. 拥有你的“最终”存储桶
  2. 为每次上传创建一个临时存储桶(如果您担心成本,则为每 1000 个存储桶 1c)
  3. 上传到临时存储桶
  4. 创建随机名称,检查最终存储桶中是否不存在(即每 1000 次检查 1c)
  5. 将文件复制到最终存储桶(使用新名称)
  6. 删除上传的文件和存储桶。
  7. 定期清理文件上传未完成的存储桶。

【讨论】:

  • 我在哪里设置那段代码?就在 $params->key = 的上方。根据您的变量 $filanem,它是一个范围,因为如果我这样做 $params->key = rand_string(5).'${filename}';它返回 Dogfilename.png。如果我误解了“范围”,请纠正我。
  • 好的,现在我明白你的意思了。我会尝试这样做。我需要找到 putObject 的确切位置
  • 还有一点,可以正常上传,上传的时候重命名?
  • 正如问题所述,它已上传到 Amazon S3,因此我无法做到这一点。我可以上传、复制、替换、删除以前的(这将花费我 4 个连接而不是 1 个)。
  • 是的,你可以。上传到 S3 后,您可以“复制”文件,然后他们会删除旧文件。全部在亚马逊文档中。
【解决方案2】:
$fileSplit = explode('.',$filename);
$extension = '.'.$fileSplit[count($fileSplit) - 1];

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

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

【讨论】:

  • 我将 $filename 更改为 '${filename}' 我得到了这个:3Sf5f.myimage.png
  • 这意味着在某个地方,您的$filename 正在失去其结构。进一步查看您的代码,您是否在任何地方更改 $filename
  • 请注意,我将 $extension 附加到 params 键,而不是 $filename。如果您想在获得扩展名后覆盖文件名,只需 $filename = $extension.
  • 由于某种原因,整个文件中不存在文件名。整个应用基于 1 个文件。
  • 我应该看哪几行,这个类是巨大的。
【解决方案3】:

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

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

【讨论】:

    【解决方案4】:

    如果你要上传图片试试这个

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

    【讨论】:

    • 你有权限修改html表单文件吗?
    • 我确实有访问权限,但先生,您的回答一无所获。
    • 变量$file必须包含上传文件的临时位置。
    • $file 在这种情况下是$_FILES['form_field_name']['tmp_name'] 的内容
    【解决方案5】:

    如果你发生了什么:

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

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

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

    那么你应该可以做到这一点:

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

    【讨论】:

    • 我玩弄了一些变量和 $filename = "${filename}";相当于说 $filename = $filename;它根本不做任何事情,您正在设置一个等于自身的变量。变量 $filename 甚至从未在该函数中设置 (getHttpUploadPostParams())。我从函数的 cmets 猜测文件名在 $headers 的元素中。尝试做一个 print_r($headers);死; $filename 之前 = "${filename}";看看你得到了什么;
    • 错了。 $filename = "${filename}";与 $filename = $filename 不同。因为我使用 $filename 进一步向下,第一种方式我得到实际的文件名,第二种方式我没有。
    • 创建一个php文件并把它放进去是一样的: $filename = 'test'; $filename2 = "${文件名}";回声“文件名2:$文件名2”;您将获得“filename2: test”作为输出。我了解您没有得到相同的结果,但是您的代码中还有其他问题。你做了 print_r($headers);如果是的话,你得到了什么?
    【解决方案6】:

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

    假设您在 $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);  
    

    【讨论】:

    • @jQuerybeast 对不起。我改变了那个。我写代码太快了。 ://
    • 这可行,但事实并非如此。如果我将 image.png 更改为“$filename”(返回实际文件名),则不返回任何内容。
    • @jQuerybeast Nop。您将 image.png 重命名为 $new_name,因此它是一个附加扩展名的随机字符串。
    • 你怎么知道它会是.png?显然我的问题是针对各种扩展。
    • @jQuerybeast 扩展(在本例中为 png)从全名分解,然后附加到新的字符串名称。
    【解决方案7】:

    好的,这是我在服务器端获取扩展时遇到问题时使用的另一种尝试。我所做的是,我使用 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'] 作为扩展名。希望有帮助。如果您在执行此操作时遇到任何问题,请告诉我

    【讨论】:

    • 等等 $params->key = $filename;我会叫什么?
    • 根据策略无效:额外输入字段:ext
    • 你可以直接使用这个 $params->key = $filename.$_POST['ext'];但我看到你不能添加额外的字段。在这种情况下,这个解决方案是不可能的。这里扩展被评估并作为表单数据的一部分发送。
    【解决方案8】:

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

    $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

    【讨论】:

    • 短号或长号不要太麻烦。同样,如果您已阅读所有答案等,您的答案将不起作用。如果我上传没有扩展名的文件怎么办?
    • 所以问题出在s3上传类,我从来没有使用过这个类,但我用过这个:Zend Framework s3类(你可以在独立模式下使用它)如果你是,试试绝望...framework.zend.com/manual/en/zend.service.amazon.s3.html
    • 该课程是否允许我随时重置为任何内容?
    • “重置”是什么意思?
    • 我真的很抱歉我可能在想别的东西。我的意思是,该类是否允许我将文件名更改为任何随机屏幕和文件扩展名?因为看来我的问题来自亚马逊。
    【解决方案9】:

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

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

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

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

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

    【讨论】:

      【解决方案10】:

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

      $ext = pathinfo($filename, PATHINFO_EXTENSION);
      

      将返回扩展部分(不带'.')

      【讨论】:

      • 您确认您的 $filename 变量是否为有效字符串?
      • 我做到了。我不确定我的确认是否正确。如果我执行 $filename = 'someimage.png',我会得到它的文件扩展名
      • AFAICT,您的问题似乎与此变量有关,然后是获取文件扩展名部分。
      • 这是整个文件:pastebin.com/EXfJwpny 这是我的应用程序中唯一存在的类。搜索 $filename。类中只有一个存在
      • 如果你的 $filename 在使用前没有定义,你怎么期望它包含有效的数据?!? $filename = '{$filename}'; = string(11) "${filename}"
      【解决方案11】:

      一个重命名文件和计算扩展名的简单解决方案

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

      【讨论】:

      • 警告:implode() [function.implode]:在您的编辑中传递的参数无效
      • 哇。我明白了:gVcwVcfcd208495d565ef66e7dff9f98764da。请注意文件扩展名仍然丢失。这令人沮丧
      • $filename 中有一个扩展名,因为我自己使用它我确实得到了结果
      • 是的,但您没有将其分配给 $file_name - 根据您的上述代码。在您的源代码中查看我的更改。
      • 这有点令人困惑。第二部分应该放在哪里?
      【解决方案12】:

      这个怎么样?

      $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
      

      【讨论】:

      • 在第一行之后尝试 vardump($temp)。也许你的字符串中有一些东西通常不会显示出来。
      猜你喜欢
      • 1970-01-01
      • 2019-06-19
      • 2013-04-10
      • 1970-01-01
      • 2011-06-28
      • 1970-01-01
      • 2016-12-05
      • 1970-01-01
      • 2013-05-26
      相关资源
      最近更新 更多