2009-03-24 35 views
44

Sử dụng PHP, được cung cấp URL, làm cách nào để xác định đó có phải là hình ảnh không?cách tốt nhất để xác định xem URL có phải là hình ảnh trong PHP

Không có ngữ cảnh cho URL - nó chỉ ở giữa tệp văn bản thuần túy hoặc có thể chỉ là một chuỗi.

Tôi không muốn chi phí cao (ví dụ: đọc nội dung của URL) vì điều này có thể được gọi cho nhiều URL trên một trang. Với hạn chế này, không cần thiết phải xác định tất cả các hình ảnh, nhưng tôi muốn có một dự đoán khá tốt.

Hiện tại tôi chỉ đang xem phần mở rộng của tệp, nhưng có vẻ như có cách nào tốt hơn.

Dưới đây là những gì tôi hiện có:

function isImage($url) 
    { 
    $pos = strrpos($url, "."); 
    if ($pos === false) 
     return false; 
    $ext = strtolower(trim(substr($url, $pos))); 
    $imgExts = array(".gif", ".jpg", ".jpeg", ".png", ".tiff", ".tif"); // this is far from complete but that's always going to be the case... 
    if (in_array($ext, $imgExts)) 
     return true; 
    return false; 
    } 

Edit: Trong trường hợp đó là hữu ích cho bất kỳ ai khác ở đây là chức năng chính thức sử dụng kỹ thuật này từ câu trả lời Emil H:

function isImage($url) 
    { 
    $params = array('http' => array(
        'method' => 'HEAD' 
       )); 
    $ctx = stream_context_create($params); 
    $fp = @fopen($url, 'rb', false, $ctx); 
    if (!$fp) 
     return false; // Problem with url 

    $meta = stream_get_meta_data($fp); 
    if ($meta === false) 
    { 
     fclose($fp); 
     return false; // Problem reading data from url 
    } 

    $wrapper_data = $meta["wrapper_data"]; 
    if(is_array($wrapper_data)){ 
     foreach(array_keys($wrapper_data) as $hh){ 
      if (substr($wrapper_data[$hh], 0, 19) == "Content-Type: image") // strlen("Content-Type: image") == 19 
      { 
      fclose($fp); 
      return true; 
      } 
     } 
    } 

    fclose($fp); 
    return false; 
    } 

Trả lời

28

Bạn có thể sử dụng yêu cầu HTTP HEAD và kiểm tra loại nội dung. Đây có thể là một sự thỏa hiệp tốt. Nó có thể được thực hiện bằng cách sử dụng PHP Streams. Wez Furlong có số article cho biết cách sử dụng phương pháp này để gửi yêu cầu đăng, nhưng có thể dễ dàng điều chỉnh để gửi yêu cầu HEAD thay thế. Bạn có thể truy xuất tiêu đề từ phản hồi http bằng cách sử dụng stream_get_meta_data().

Tất nhiên điều này không thực sự là 100%. Một số máy chủ gửi tiêu đề không chính xác. Tuy nhiên, nó sẽ xử lý các trường hợp hình ảnh được gửi qua tập lệnh và phần mở rộng tệp chính xác không khả dụng. Cách duy nhất để thực sự chắc chắn là thực sự truy xuất hình ảnh - hoặc là tất cả, hoặc một vài byte đầu tiên, theo đề xuất của thomasrutter.

+0

Tôi sẽ không mô tả điều này như là bằng chứng chống đạn. Trình duyệt bỏ qua loại nội dung cho hình ảnh gặp phải trong phần tử thomasrutter

+0

Có. Tôi đồng ý. Tôi sẽ thay đổi ngôn ngữ. :) Tôi nghĩ rằng đó là một trong những tốt nhất có thể làm mà không lấy các nội dung thực tế của url, mặc dù. –

+0

(downvote loại bỏ) yeah Tôi nghĩ rằng đây là một lựa chọn phong nha bây giờ :) – thomasrutter

13

Có một vài cách tiếp cận khác nhau.

  • Ngửi nội dung bằng cách tìm số ma thuật ở đầu tệp. Ví dụ: GIF sử dụng GIF87 hoặc GIF89 làm năm byte đầu tiên của tệp (trong ascii). Rất tiếc, điều này không thể cho bạn biết nếu có lỗi trong hình ảnh hoặc nếu hình ảnh chứa nội dung độc hại. Dưới đây là một số số ma thuật cho các loại tệp hình ảnh khác nhau (vui lòng sử dụng các loại tệp này):

     
    "\xff\xd8\xff" => 'image/jpeg', 
    "\x89PNG\x0d\x0a\x1a\x0a" => 'image/png', 
    "II*\x00" => 'image/tiff', 
    "MM\x00*" => 'image/tiff', 
    "\x00\x00\x01\x00" => 'image/ico', 
    "\x00\x00\x02\x00" => 'image/ico', 
    "GIF89a" => 'image/gif', 
    "GIF87a" => 'image/gif', 
    "BM" => 'image/bmp', 
    

    Ngửi nội dung như thế này có thể phù hợp với yêu cầu của bạn nhất; bạn sẽ chỉ phải đọc và do đó tải xuống một vài byte đầu tiên của tệp (qua tiêu đề).

  • Tải hình ảnh bằng thư viện GD để xem hình ảnh có tải không có lỗi. Điều này có thể cho bạn biết nếu hình ảnh hợp lệ, không có lỗi hay không. Thật không may, điều này có thể không phù hợp với yêu cầu của bạn vì nó yêu cầu tải xuống hình ảnh hoàn chỉnh.

  • Nếu bạn thực sự không muốn thực hiện yêu cầu HTTP cho hình ảnh, thì quy tắc này sẽ cả đánh hơi và nhận tiêu đề HTTP. Bạn có thể, tuy nhiên, cố gắng xác định xem một cái gì đó là một hình ảnh của bối cảnh trong đó nó được liên kết. Một cái gì đó liên kết bằng cách sử dụng một thuộc tính src trong một yếu tố img < là gần như chắc chắn là một hình ảnh (hoặc một nỗ lực tại XSS, nhưng đó là một câu chuyện khác). Điều này sẽ cho bạn biết nếu một cái gì đó được dự định như là một hình ảnh. Nó sẽ không cho bạn biết liệu hình ảnh thực sự có sẵn hay hợp lệ; bạn sẽ phải tìm nạp ít nhất phần nhỏ đầu tiên (số đầu trang hoặc số ma thuật) của URL hình ảnh để tìm thấy điều đó.

Thật không may, có thể tệp là cả ảnh hợp lệ cũng như tệp ZIP chứa nội dung độc hại có thể được thực thi dưới dạng Java bởi trang web độc hại - xem the GIFAR exploit. Bạn gần như chắc chắn có thể ngăn chặn lỗ hổng này bằng cách tải hình ảnh trong thư viện như GD và thực hiện một số bộ lọc không tầm thường, như làm mềm hoặc mài nó một lượng nhỏ (tức là sử dụng bộ lọc chập) và lưu nó vào một tập tin mới mà không cần chuyển bất kỳ siêu dữ liệu nào.

Cố gắng xác định xem một cái gì đó là một hình ảnh theo kiểu nội dung của nó một mình là khá không đáng tin cậy, gần như không đáng tin cậy như kiểm tra phần mở rộng tập tin. Khi tải hình ảnh bằng cách sử dụng phần tử img <, trình duyệt sẽ tìm kiếm chuỗi ma thuật.

+0

Cảm ơn câu trả lời chi tiết nhưng đối với ứng dụng của tôi có khả năng đọc hàng trăm hình ảnh trước khi hiển thị một trang có thể sẽ có quá nhiều chi phí. – danio

+0

Bạn đã đọc điểm chấm thứ ba của tôi chưa? Bạn không chắc chắn liệu mình có nhận được hình ảnh từ thẻ thomasrutter

+0

Ah - chỉ thấy bạn đang nhận được các URL hình ảnh từ một tệp văn bản. Một số loại yêu cầu HTTP ánh sáng, giống như điểm dấu chấm đầu tiên của tôi, có lẽ sẽ là cần thiết. Lưu ý rằng chi phí không cao hơn đáng kể so với phương thức HTTP HEAD. – thomasrutter

11
if(is_array(getimagesize($urlImg))) 
    echo 'Yes it's an image!'; 
+9

getimagesize() tải xuống toàn bộ tệp hình ảnh. – user191688

3

Chỉnh sửa: Đối với hình ảnh tĩnh có phần mở rộng hình ảnh phổ biến.

<?php 
$imgExts = array("gif", "jpg", "jpeg", "png", "tiff", "tif"); 
$url ='path/to/image.png'; 
$urlExt = pathinfo($url, PATHINFO_EXTENSION); 
if (in_array($urlExt, $imgExts)) { 
    echo 'Yes, '.$url.' is an Image'; 
} 

?> 
+0

Đây là hình ảnh https://contulmeu.moldcell.md/ps/selfcare_uni/crypt/cryptographp.inc.php nhưng url này sẽ không vượt qua xác thực của bạn! Bạn sai rồi. – Jekis

+0

Như Jenechka chỉ ra kỹ thuật này là rất hạn chế: 1. nó giả định tất cả các tập tin hình ảnh có extensons hợp lệ; 2. nó không có hỗ trợ cho các định dạng hình ảnh tác giả đã không nghĩ về/được tạo ra sau khi chương trình được viết – danio

1

chúng tôi có thể sử dụng exif_imagetype để kiểm tra loại hình ảnh, vì vậy nó không cho phép bất kỳ loại nội dung nào khác. Nó chỉ cho phép hình ảnh và chúng tôi có thể hạn chế chúng với vài loại hình ảnh, sau mã mẫu cho thấy cách cho phép loại hình ảnh GIF.

if (exif_imagetype('image.gif') != IMAGETYPE_GIF) { 
    echo 'The picture is not a gif'; 
} 

Bạn có thể sử dụng sau đây loại hình ảnh,

IMAGETYPE_GIF 
IMAGETYPE_JPEG 
IMAGETYPE_PNG 
IMAGETYPE_SWF 
IMAGETYPE_PSD 
IMAGETYPE_BMP 
IMAGETYPE_TIFF_II (intel byte order) 
IMAGETYPE_TIFF_MM (motorola byte order) 
IMAGETYPE_JPC 
IMAGETYPE_JP2 
IMAGETYPE_JPX 
IMAGETYPE_JB2 
IMAGETYPE_SWC 
IMAGETYPE_IFF 
IMAGETYPE_WBMP 
IMAGETYPE_XBM 
IMAGETYPE_ICO 

biết thêm chi tiết: link

+1

Đúng, nhưng 'exif_imagetype' không hoạt động với URL. – fredrikekelund

7

Ngoài câu trả lời Emil H:

Sử dụng get_headers() để kiểm tra các kiểu nội dung của một url mà không cần tải xuống toàn bộ tệp với getimagesize()

$url_headers=get_headers($url, 1); 

    if(isset($url_headers['Content-Type'])){ 

     $type=strtolower($url_headers['Content-Type']); 

     $valid_image_type=array(); 
     $valid_image_type['image/png']=''; 
     $valid_image_type['image/jpg']=''; 
     $valid_image_type['image/jpeg']=''; 
     $valid_image_type['image/jpe']=''; 
     $valid_image_type['image/gif']=''; 
     $valid_image_type['image/tif']=''; 
     $valid_image_type['image/tiff']=''; 
     $valid_image_type['image/svg']=''; 
     $valid_image_type['image/ico']=''; 
     $valid_image_type['image/icon']=''; 
     $valid_image_type['image/x-icon']=''; 

     if(isset($valid_image_type[$type])){ 

      //do something 

     } 
    } 
+1

'$ url_headers ['Content-Type']' rất có thể là một mảng, và nên được kiểm tra trước khi gán '$ type'. Ngoài ra, hãy cẩn thận để theo dõi thứ tự bạn đọc mảng (nếu chỉ quan tâm đến loại nội dung cuối cùng, sau khi tất cả chuyển hướng, thì bạn sẽ muốn mục cuối cùng trong mảng) – Birrel

2

Tương tự như một số câu trả lời nhất định nhưng có logic hơi khác.

$headers = @get_headers($url, 1); // @ to suppress errors. Remove when debugging. 
if (isset($headers['Content-Type'])) { 
    if (strpos($headers['Content-Type'], 'image/') === FALSE) { 
    // Not a regular image (including a 404). 
    } 
    else { 
    // It's an image! 
    } 
} 
else { 
    // No 'Content-Type' returned. 
} 

@ là error control operator.

Lưu ý rằng chúng tôi đã sử dụng toán tử "strict" === FALSE trong điều kiện vì strpos($headers['Content-Type'], 'image/') trả lại 0 trong trường hợp sử dụng của chúng tôi nếu kim được tìm thấy trong haystack. Với tính năng nhập kiểu bằng cách sử dụng == có thể diễn giải sai là FALSE.

0

Giải pháp nhanh cho bị hỏng hoặc không phải hình ảnh tìm thấy liên kết
tôi khuyên bạn không sử dụng getimagesize() bởi vì nó sẽ ảnh tải về 1 sau đó nó sẽ kiểm tra kích thước hình ảnh + nếu sẽ không hình ảnh này sau đó nó sẽ ném ngoại lệ để sử dụng mã dưới đây

if(checkRemoteFile($imgurl)) 
{ 
//found url, its mean 
echo "this is image"; 
} 

function checkRemoteFile($url) 
{ 
    $ch = curl_init(); 
    curl_setopt($ch, CURLOPT_URL,$url); 
    // don't download content 
    curl_setopt($ch, CURLOPT_NOBODY, 1); 
    curl_setopt($ch, CURLOPT_FAILONERROR, 1); 
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); 
    if(curl_exec($ch)!==FALSE) 
    { 
     return true; 
    } 
    else 
    { 
     return false; 
    } 
} 

Lưu ý: mã này giúp bạn xác định hiện hành bị hỏng hoặc không tìm thấy hình ảnh url này sẽ không giúp bạn xác định loại hình ảnh hoặc tiêu đề

Các vấn đề liên quan