2010-07-18 123 views
2

過去幾個月,我一直在使用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的圖像位於服務器上?我是否應該在創建新圖像後清除此問題?

回答

3

通過在文件名中的用戶發送不可信任或依靠擴展。有些用戶認爲將'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

4

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

有幾件事情可以讓你的生活變得更輕鬆。下面列出的事情不是讓你失望,而是讓你學習!

你把一切從第一.作爲擴展名,因爲它現在站立。因此,有人在文件名中輸入.時出錯。這可以做得更好。

$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

4

富有,我已經做了類似的,主要是與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

3

我遇到了一個我允許用戶上傳的網站的問題:文件將正確上傳,然後在我重新加載頁面後有時不會顯示。

我發現我沒有被正確改變文件的權限和服務器主要是標記事物對於大多數用戶非法....

我用CHMOD功能上傳後更改權限和那麼它的工作可靠。

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