使用PHP上传图片

问题描述:

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

既然这样,我已经得到了全过程的工作在本地和托管服务优良,但该网站上线之前,我有一对夫妇对我做的方式问题。

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

$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的图像位于服务器上?我是否应该在创建新图像后清除此问题?

通过在文件名中的用户发送不可信任或依靠扩展。有些用户认为将'jpg'改为'gif'会使其成为gif等。

我建议使用getimagesize首先检查它是否是有效图像并获取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,所以他们被错误地拒绝了。我想出了这个邪恶的黑客至少检测到的类型,并给他们一个尝试...

[email protected]($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 
     } 
    } 
+0

我不认为文件名可能不好。感谢关于getimagesize的提示。 至于图像通道,我不知道CYMK会造成麻烦。 非常有用的信息,谢谢! – chudley 2010-07-18 21:52:38

+1

文件类型的好处。一个有趣的PHP怪癖是,你可以说'imagecreatefromstring()',然后你不必知道类型 - 相当奇怪,但是是真的。所以你可以使用'$ imagedata = file_get_contents($ filename);'来加载图像。 (注意共享主机环境,但内存可能有限;这将额外消耗2 MB) – mvds 2010-07-18 22:34:18

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

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

你把一切从第一.作为扩展名,因为它现在站立。因此,有人在文件名中输入.时出错。这可以做得更好。

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

会给你扩展名,小写,没有点,这意味着你不必测试JPG和JPG等。(实际上,虽然上传浏览器会告诉你文件类型,不管扩展名是什么,但现在只需跳过 - 扩展测试将会很好)

图像阅读与if JPG else if PNG else GIF不是很干净:你应该测试gif以及只有然后转到“其他”类别,只是抛出一个错误。 (这意味着你可以放弃你之前做过的检查!)

当你说$source = imagecreatefrom...($filename)你最好预先用@来避免在损坏的图像上发出警告(通常,不要使用@,但在这种情况下,你无法知道如果图像损坏)。然后经常检查返回值(总是这样),像

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

现在的图像加载,所以如果已知大小;您不必再次查询文件。而不是getimagesize()你可以使用imagesx($source)imagesy($source)

这足以解决现在,我想。 ;-)

编辑:微小的问题btw与兰特(0,9)在文件名意味着机会很大,如果多个客户端在同一秒内上传多个文件混杂起来的文件。 (每秒有11次上传,你肯定有问题)

+0

感谢您的答复! 完整的中止错误是一个我知道但尚未处理,但会看看你建议的代码,谢谢! 至于如果JPG其他等,这听起来像是一个更好的方式来处理if语句,我会尽快实现。 $源和@符号修复看起来也不错。我没有真正考虑腐败的图像。谢谢你的提示! 现在你已经提到它,在同一秒内的多个上传并不像想象中那么遥远!我会改变的! 谢谢你的回应! – chudley 2010-07-18 21:35:47

富有,我已经做了类似的,主要是与iMagick。 GD在功能上是相似的,所以我期望没有问题。我的网站每周处理一百张图片没有问题,并且可靠地服务于每周1k左右。我在后台执行了所有的处理,因为它看起来像你在这个例子中做的那样,所以很少担心大量的入站流量(比如DIGG上的拾取)碾碎你的服务器。

您通过允许上传任何类型而让自己面临漏洞的最大挑战。你可能已经听说过IT安全人士说,防止黑客攻击的唯一方法就是脱离网络......它就是这样。我不会生活在完全的恐惧之中,因为它看起来像你已经采取了公平的步骤来审计文件类型和大小。另一个考虑是查看服务器上的权限 - 打开仅写入服务器的用户代理的目录,并阻止浏览到目录以获得额外的安全性。如果您希望双倍安全,请写信给另一个帐户(如果您有这样的帐户),以限制暴露于您的代码。这不是必要的,但如果您有疑虑,这是一个很好的额外步骤。最后,将您的上传器放在一个带有验证码的简单密码系统后面,以阻止自动漏洞检查器......通过一个简单的注册步骤为用户提供免费访问。这是一个小UI的麻烦,但可以使所有差异安全明智。

如果用户文件超过2MB限制,我可能会考虑停止进程。这就是我。如果某些用户在您的服务器上尝试强制使用.jpg文件扩展名的错误文件,则您不希望系统崩溃并进行刻录。如果您要托管自己,或者如果您通过Meg支付,带宽可能只是一个问题 - 确保这会提高您的带宽,但即使每天有一百次上传,您可能不会推动标准服务器超出其意义,除非访问该站点的相应通信量为数千。大多数主机将允许您监视服务器负载。我已经与HostGator亲自在廉价上运气不错

无限期地停留在服务器上,假设没有什么不好的事情发生在您的账户上,并且您支付账单。出于这个原因,请确保将上传文件夹与其他任何内容分开 - 一旦上传了几百个文件,它就变成了一场噩梦。经常备份,以保证安全。

+0

是的,任何一种用户交互都不完全值得信赖!我有一个简单的用户注册过程,所以希望这会吓倒垃圾邮件。 用户代理访问该文件夹的好主意。我会做一些研究。 很高兴听到您正在处理的带宽。我现在可以松一口气了!但对于服务器上的文件,我的意思是用户将上传的原始文件。那么这个$ _FILES变量所持有的图像是否合理呢?我会更新原文,尝试更好地解释一下。 感谢您的回应! – chudley 2010-07-18 21:41:18

+0

我不确定您是否可以在上传过程中检测文件是否超过了2MB的限制 - 在过去查看此内容时,它似乎非常复杂。 – JAL 2010-07-18 21:42:46

+0

亚历克斯,当然可以。默认情况下,PHP上传到一个临时文件夹。您在执行move_uploaded_file之前检查。正如Rich提到的,他在获得上述代码之前进行文件大小检查。 – bpeterson76 2010-07-18 23:36:14

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

我发现我没有被正确改变文件的权限和服务器主要是标记事物对于大多数用户非法....

我用CHMOD功能上传后更改权限和那么它的工作可靠。

下面是关于它的文章(不是我的文章,但它是有用的): http://drupal.org/node/34028