使用PHP上传图像

时间:2010-07-18 20:43:57

标签: php mysql image upload

在过去的几个月里,我一直在使用PHP和MySQL构建一个活动推广网站,任何人都可以注册并添加他们的本地活动的详细信息以及我调整大小的海报。

就目前而言,我已经让整个过程在本地和托管服务上运行良好,但在网站上线之前,我对我的方式有几个问题。

这是我用来处理图片上传的功能代码。我在本节之前检查了文件大小。

$extension = substr($filename, strpos($filename,'.'), strlen($filename)-1); 
$filetypes = array('.jpg', '.jpeg', '.gif', '.bmp', '.png', '.JPG', '.PNG', '.JPEG', '.GIF', '.BMP');
if($_FILES['image']['error'] == 4){
  $error = "No image";
  return $error; 
}
else if(($_FILES['image']['error'] == 2) || ($_FILES['image']['error'] == 1)){
  $error = "File size too big";
  return $error;
}
else if(!in_array($extension, $filetypes)){
  $error = "This isn't an image that is supported";
  return $error;
}
else if(($_FILES['image']['error'] == 7) || ($_FILES['image']['error'] == 3)){
  $error = "Error occurred. Try again";
  return $error;
}
else{
  if(($extension == '.jpg') || ($extension == '.jpeg')){
    $source = imagecreatefromjpeg($uploaded);
  }
  else if($extension == '.png'){
    $source = imagecreatefrompng($uploaded);
  }
  else{
    $source = imagecreatefromgif($uploaded);
  }
  list($width, $height) = getimagesize($uploaded);
  $ratio = $width / $height;
  $new_width = 300;
  $new_height = round(300 / $ratio); 
  $canvas = imagecreatetruecolor($new_width, $new_height);
  imagecopyresampled($canvas, $source, 0, 0, 0, 0, $new_width, $new_height, $width,       $height);
  $name = date("dmyHis").rand(0, 9);
  $path = $_SERVER[ 'DOCUMENT_ROOT' ] . '/images/uploaded/'.$name.'.jpg';
  $new_image = imagejpeg($canvas, $path,  100);
  $poster['name'] = $name.'.jpg';
  $poster['width'] = $new_width;
  $poster['height'] = $new_height;
  return $name.'.jpg';
}

就目前而言,我知道或者还没有完全研究过一些错误,例如某些图像从imagecreatefromwhatever中抛出错误,以及图像名称是否有“。”在它中,它也会引发错误。

完成此过程后,我会将图像名称保存到MySQL中的“海报”字段中,该字段将用于在查看时从文件夹中获取正确的图像。

我真正想知道的是,如果图像上传可能会遇到任何其他问题吗?

  • 我预计会有相当多的流量,因此这段代码会在大量使用时运行良好吗?
  • 我还应该注意其他任何陷阱或事情吗?
  • 我是否使用最好的方法来完成工作?
  • 目前我的文件大小限制是2MB,这太高了吗?
  • 即使用户上传的内容超过2MB,脚本仍会运行,我认为该文件将上传到服务器进行名称剥离和文件大小比较等,这将如何影响我的带宽使用?
  • 原始文件在服务器上保留多长时间?

如果有人对这个问题有任何好的阅读,我会非常感激!

谢谢。

编辑:格式化。

编辑2:我没有说清楚原始文件。我的意思是我使用$ _FILES变量访问的原始文件。说它是1.9MB,在整个我摆弄扩展的时候,服务器上会有1.9MB的价值吗?一旦我创建了新图像,我应该清除这个吗?

4 个答案:

答案 0 :(得分:4)

首先,做得好,看起来你在其中投入了大量的精力。

有一些事情可以让你的生活变得更轻松。下面列出的事情不是让你失望,而是让你学习!

您将第一个.中的所有内容作为现在的扩展名。因此,有人在文件名中放入.时出错。这可以做得更好。

$extension = '';
if ( preg_match("/\\.([a-z]+)$/i",$filename,$match) )
{
    $extension = strtolower($match[1]);
}

将为您提供小写的扩展名,不带点,这意味着您不必测试JPG以及jpg等(实际上,上传浏览器时会告诉您文件类型,无论是扩展,但我们暂时跳过它 - 扩展测试会很好)

使用if JPG else if PNG else GIF的图像读取并不是很干净:你也应该测试gif,只有然后转到“else”类别,并且只是抛出一个错误。 (这意味着你可以放弃之前的支票!)

当你说$source = imagecreatefrom...($filename)时,你最好在它前面添加@来避免对损坏的图像发出警告(通常,不要使用@,但在这种情况下你不知道图像是否已损坏)。然后总是检查返回值(总是这样做),比如

$source = @imagecreatefrompng($filename);
if ( !$source ) return "Error parsing image";

图片现在已加载,因此大小已知;您不必再次查询该文件。您可以使用getimagesize()imagesx($source)

代替imagesy($source)

我认为这足以解决现在的问题。 ; - )

编辑:在文件名中使用rand(0,9)的小问题btw意味着如果多个客户端在同一秒内上传,则文件混乱的可能性很大。 (每秒上传11次你肯定有问题)

答案 1 :(得分:4)

Rich,我做的很相似,主要是iMagick。 GD在功能上是相似的,所以我希望没有问题。我的网站每周处理一百张图片,没有问题,可靠地服务到~1k /周。我在后端进行了所有处理,因为看起来你正在这个例子中做,所以很少担心重度入站流量(例如DIGG的拾取)会破坏你的服务器。

通过允许任何类型的上传,您可以通过漏洞打开自己的最大挑战。您可能听说过IT安全人员说,防止黑客入侵的唯一方法就是下网......就像那样。我不会完全害怕,因为看起来你采取了公平的步骤来审核文件类型和大小。另一个考虑因素是查看服务器上的权限 - 打开目录只写入服务器的用户代理并阻止浏览到目录以获得额外的安全性。如果您想要倍加安全,请写入其他帐户(如果您有这样的帐户)以限制您的代码曝光。这没有必要,但如果您有疑虑,这是一个很好的额外步骤。最后,将您的上传器放在一个带有验证码的简单密码系统后面,以阻止自动漏洞检查程序....通过简单的注册步骤为用户提供免费访问。这是一个小的UI麻烦,但可以在安全方面做出一切改变。

如果用户文件超过2mb限制,我可以考虑停止该过程。这就是我。如果某些用户尝试在服务器上强制使用带有.jpg文件扩展名的错误文件,则不希望系统崩溃和刻录。带宽可能只是一个问题,如果你是自己托管,或者如果你是通过巨额支付,这会提高你的带宽,但即使每天上传一百次,你也可能不会推动标准服务器超出其手段,除非访问该站点的相应流量是数千。大多数主机将允许您监视服务器负载。我和HostGator一起在便宜的地方运气好运

文件无限期地保留在服务器上,假设您的帐户没有任何不良影响并且您支付账单。因此,请确保将上传文件夹与任何其他内容分开 - 一旦上传了几百个文件,就会变成一场噩梦。经常备份,只是为了安全。

答案 2 :(得分:3)

我遇到了一个允许用户上传的网站的问题:文件会正确上传,然后在我重新加载页面后就不会显示。

我发现我没有正确地更改文件的权限,而且服务器主要是将标记为大多数用户的非法标记....

我使用CHMOD函数在上传后更改权限,然后它可靠地运行。

这是一篇关于它的文章(不是我的文章,但它很有用): http://drupal.org/node/34028

答案 3 :(得分:3)

用户在文件名中发送的扩展名不可信任或依赖。一些用户认为将'jpg'改为'gif'会使它成为gif等等。

我建议使用getimagesize FIRST检查它是否是有效图像并获取exif类型。不要担心提取扩展,因为它没用。 exif类型将在getimagesize返回的数组的2中。

此外,CYMK图像是一个问题。有些人设法上传CYMK jpeg。检查通道将检测这些图像。它应该是3,RGB。

$image_info=getimagesize($your_image_file);
if($image_info['channels']==4)
  {
  //it's invalid - cymk
  //browsers cannot display these images. It might be possible to convert them to RGB explicitly...
  }

$real_exif=$image_info[2];
if($real_exif>0 && $real_exif<4){
 //it is a png, gif or jpg
 }

exif类型作为常量返回,如IMAGETYPE_GIF,数字上,1是gif,2是jpg,3是png。您可以使用image_type_to_extension转换为文本文件扩展名。

现在,有时我发现getimagesize无法为有效的图像找到exif类型,并且可以使用imagemagick / GD进行处理。它没有为这些返回EXIF,因此被错误地拒绝了。我想出了这个卑鄙的黑客,至少要检测一下这个类型并试一试......

  $handle=@fopen($temp_name,'r');
  if($handle)
    {
    $chars=fread($handle,24);
    if(stripos($chars,'jfif')!==false)
        {$type=2;} // found a jpg
    elseif(stripos($chars,'png')!==false)
      {$type=3;} // found a png
    elseif(stripos($chars,'gif')!==false)
      {$type=1;} // found a gif
    else
      {
      //file type could not be determined
      }
    }