Определение ширины и высоты файла JPEG

В «Эгее» есть какой-то плавающий баг, из-за которого иногда не заливаются файлы JPEG. Илья, автор «Эгеи», пытался с ним разобраться, но до конца понять проблему не смог.

Пока рабочая теория такая, что функция ПХП getimagesize (она возвращает тип изображения и его высоту с шириной) иногда выдаёт неправильные значения.

Опровергнуть или подтвердить эту теорию не так-то и просто — баг проявляется очень редко, но можно попытаться считывать размеры каким-то другим способом и посмотреть исчезнет проблема или нет.

Вот Илья и попросил написать меня такой код на чистом ПХП. Надеюсь это позволит продвинуться в изучении проблемы — код вошёл в одиннадцатую версию «Эгеи».

Выложу его у себя тоже, чтобы не потерялся, ну и может кому-то ещё пригодится:

function readJPEGWxH(string $filename): array
{
    $file = new SplFileObject($filename);

    if (!$file->flock(LOCK_SH)) {
        throw new RuntimeException('Cannot lock the file');
    }

    if ($file->fread(2) !== "\xFF\xD8") {
        throw new RuntimeException('Unknown format');
    }

    for (;;) {
        $decoded = @unpack('H4segment/nlen', $file->fread(4));

        if ($decoded === false) {
            throw new RuntimeException('Unknown format');
        }

        ['segment' => $segment, 'len' => $len] = $decoded;

        // SOFn (Start Of Frame)?
        if (preg_match('/^ffc[0-3]/', $segment)) {
            $segdata = @unpack('Cbits/nheight/nwidth', $file->fread(5));

            if ($segdata === false) {
                throw new RuntimeException('Unknown format');
            }

            return [$segdata['width'], $segdata['height']];
        }

        // len = full lenght of segment except segment magic code
        if ($file->fseek($len - 2, SEEK_CUR) !== 0) {
            throw new RuntimeException('Cannot find start of frame');
        }
    }
}

Комментарии 5

SiMMmr E16/NR04δ13 № 1
возможно имело смысл и EOI («\xFF\xD9») в конце проверить, как доказательство того, что файл залит полностью )
и стоило ли так (потенциально) много читать из файла при формировании $segdata?
а не разумнее было начать с того, чтобы эти файлы с ошибкой getimagesize отловить?
Евгений Степанищев автор № 2
Ошибка нерегулярно появляется. Залит ли файл полностью сообщает PHP (надо бы, кстати, узнать у Илья проверяет ли это).

Потенциально много — это размер чанка? Необходимости нет, но и код не для хайлоада) если смущает, переделаю попозже)
SiMMmr E16/NR04δ13 № 3
ага, про эту строчку $segdata = @unpack('Cbits/nheight/nwidth', $file->fread($len — 2));
так регулярность не имеет значения — главное, файл сохранить и запомнить наличие проблемы — а там как накопится — можно уже с файлами разобраться, что в них не так или в библиотеке
Евгений Степанищев автор № 4
Ошибка нерегулярно появляется на одном и том же файле, если я правильно понял Илью