Получаем тип и размеры изображения без скачивания его целиком, используя Python

    Возникла задача профильтровать базу данных ссылок на изображения и удалить большие. В результате было найдено решение на Python, которым я поделюсь под хабракатом:

    image

    Сам скрипт + необходимая для работы библиотека ReseekFile

    Вся прелесть решения в том, что для получения информации о типе и размерах изображения(jpg, png, gif) достаточно скачать первых 24 байта файла.

    Код анализа этих 24 байт:

        # handle GIFs
        if (size >= 10) and data[:6] in ('GIF87a', 'GIF89a'):
            # Check to see if content_type is correct
            content_type = 'image/gif'
            w, h = struct.unpack("<HH", data[6:10])
            width = int(w)
            height = int(h)
    
        # See PNG 2. Edition spec (http://www.w3.org/TR/PNG/)
        # Bytes 0-7 are below, 4-byte chunk length, then 'IHDR'
        # and finally the 4-byte width, height
        elif ((size >= 24) and data.startswith('\211PNG\r\n\032\n')
              and (data[12:16] == 'IHDR')):
            content_type = 'image/png'
            w, h = struct.unpack(">LL", data[16:24])
            width = int(w)
            height = int(h)
    
        # Maybe this is for an older PNG version.
        elif (size >= 16) and data.startswith('\211PNG\r\n\032\n'):
            # Check to see if we have the right content type
            content_type = 'image/png'
            w, h = struct.unpack(">LL", data[8:16])
            width = int(w)
            height = int(h)
    
        # handle JPEGs
        elif (size >= 2) and data.startswith('\377\330'):
            content_type = 'image/jpeg'
            datastream.seek(0)
            datastream.read(2)
            b = datastream.read(1)
            try:
                while (b and ord(b) != 0xDA):
                    while (ord(b) != 0xFF): b = datastream.read(1)
                    while (ord(b) == 0xFF): b = datastream.read(1)
                    if (ord(b) >= 0xC0 and ord(b) <= 0xC3):
                        datastream.read(3)
                        h, w = struct.unpack(">HH", datastream.read(4))
                        break
                    else:
                        datastream.read(int(struct.unpack(">H", datastream.read(2))[0])-2)
                    b = datastream.read(1)
                width = int(w)
                height = int(h)
            except struct.error:
                pass
            except ValueError:
                pass
    


    Надеюсь, что этот скрипт найдет себе место в избранном хабраюзеров и послужит когда это будет необходимо :)
    Поделиться публикацией
    AdBlock похитил этот баннер, но баннеры не зубы — отрастут

    Подробнее
    Реклама
    Комментарии 14
    • +9
      Так приятно слышать, что в мире стало еще на одного человека больше, кто знает, что размеры изображения хранятся в начале файла. А еще битрейт mp3 файла бывает там же хранится. В общем, в начале файла много всего интересного обычно, иначе эти форматы не были бы binary safe.
      • +3
        В общем, в начале файла много всего интересного обычно, иначе эти форматы не были бы binary safe.
        Что-то не вижу логики.
      • +5
        буквально на днях делал тоже самое
        чтобы не изобретать велосепид можно использовать PIL

        def _fetch_image(cls, url, user_agent):
                """Return the image by URL downloading as little as possible."""
        
                request = urllib2.Request(url)
                request.add_header('User-Agent', user_agent)
                parser = ImageFile.Parser()
                response = None
                try:
                    response = urllib2.urlopen(request)
        
                    while True:
                        chunk = response.read(1024)
                        if not chunk:
                            break
        
                        parser.feed(chunk)
                        if parser.image:
                            return parser.image
                except:
                    return None
                finally:
                    if response:
                        response.close()
        


        подсмотрел здесь github.com/reddit/reddit/blob/master/r2/r2/lib/media.py
        • 0
          в моем случае еще требовались размеры картинки
          • +15
            $ curl -s -r 0-24 http://www.python.org/images/python-logo.gif| file -
            /dev/stdin: GIF image data, version 89a, 211 x 71
            так-то.
          • –17
            запрос HEAD видать придумали трусы…
            • +17
              по HEAD получается размер файла, но не размеры изображения
            • –4
              Сударь, для такой мелочи использовать внешние библиотеки — неуважение к тем, с кем Вы хотите её поделиться. У вас есть два варианта — убрать Reseek или сделать egg.
              • +18
                Три — положить болт на тех, кто думает что им кто-то должен.
              • 0
                хмммм, а еще есть такая хорошая командочка «file», про которую написано «The magic tests are used to check for files with data in particular fixed formats.»
                Причем знает не только 3 вида картинок, а намного больше всего про самые разые форматы файлов.
                • 0
                  file не всегда может узнать размеры изображения:

                  curl -s -r 0-24 http://img2-1.timeinc.net/ew/dynamic/imgs/110315/Charade_240.jpg | file -
                  
                  • +1
                    Вот еще вариант, детектит размер и не только (не чтоб чего-то доказать, а вдруг кому пригодится).
                    Правда оно потребовало чуть больший кусок от файла

                    curl -s -r 0-2000 http://img2-1.timeinc.net/ew/dynamic/imgs/110315/Charade_240.jpg | identify -
                    


                    -=>/tmp/magick-OZeNcjJh JPEG 240x320 240x320+0+0 8-bit DirectClass 2KB 0.000u 0:00.000
                    • 0
                      Опять не универсальное решение:

                      curl -s -r 0-2000 http://digitaldeconstruction.com/wp-content/uploads/2013/02/mma.jpg | identify -
                      
                • +1
                  А на JS как такое сделать, не подскажете?
                  P.S. сразу предупреждаю, что JS я юзаю только в сочетании с Qt C++, и там V8, по идее должно быть доступно все то же, что и в браузере.

                  Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.