Запретить imagejpeg () сохранять данные EXIF ​​для изображений (спецификация FileDateTime)

Наш сервер сохраняет данные EXIF ​​в каждый файл, сохраненный с imagejpeg(). Насколько мне известно, это не поведение по умолчанию (или даже возможно, исходя из того, что я читал). Но это происходит, и из-за того, что FileDateTime информация включена (и используется time сохранения), это нарушает функциональность нашей системы загрузки / утверждения (md5_file() возвращает другое значение для того же самого изображение из-за того, что FileDateTime всегда отличается).

Есть ли способ запретить imagejpeg() сохранять данные EXIF ​​для изображений по умолчанию?

Информация о сервере

  • CentOS 5
  • Parallels Plesk Panel 10.4.4
  • Версия GD: в комплекте (совместима с 2.0.34)
  • PHP 5.3

Код

<?php
public function upload_book_cover($book, $cover, $filename = NULL, $approved = NULL){
    global $c_consummo, $user;
    $approved = bool($approved, true, true);
    if(filesize($cover)>5242880){
        return false; // Too large;
    }
    $max_width = 450;
    $cover_info = getimagesize($cover);
    if(!$this->is_valid_book_cover_type($cover_info['mime'])){
        return false; // Invalid image type
    }
    $width = $cover_info[0];
    $height = $cover_info[1];
    if($width<200){
        return false; // Too small
    } elseif($width>1500){
        return false; // Too wide
    }
    $original_cover = false;
    switch($cover_info[2]){
        case IMAGETYPE_GIF:
            $original_cover = imagecreatefromgif($cover);
            break;
        case IMAGETYPE_JPEG:
            $original_cover = imagecreatefromjpeg($cover);
            break;
        case IMAGETYPE_PNG:
            $original_cover = imagecreatefrompng($cover);
            break;
        case IMAGETYPE_BMP:
            $original_cover = imagecreatefrombmp($cover);
            break;
    }
    if(!$original_cover){
        return false; // Unsupported type
    }
    if($width>$max_width){
        $new_width = $max_width;
    } else {
        $new_width = $width;
    }
    $new_height = round($height*($new_width/$width));
    $new_cover = imagecreatetruecolor($new_width, $new_height);
    if(!$new_cover){
        return false; // Could not create true color image
    }
    if(!imagecopyresampled($new_cover, $original_cover, 0, 0, 0, 0, $new_width, $new_height, $width, $height)){
        return false; // Could not copy image
    }
    if(!imagejpeg($new_cover, $cover, 100)){
        return false; // Image could not be saved to tmp file
        // This is adding *new* EXIF data to images by itself
    }
    $file_hash = md5_file($cover);
    $duplicate_book_cover = $this->find_duplicate_book_cover($book, $file_hash);
    if($duplicate_book_cover){
        return $duplicate_book_cover;
    }
    $file_id = $c_consummo->upload_file($cover, $filename);
    ...
}

person 0b10011    schedule 30.04.2012    source источник
comment
Вместо этого вы можете хешировать исходный файл.   -  person Alix Axel    schedule 11.05.2012
comment
@AlixAxel, я не хочу, чтобы данные EXIF ​​были включены (либо в исходную загрузку, либо в новую версию), так как я не хочу, чтобы файл считался другим, потому что где-то вдоль строки данные EXIF ​​были изменены (без изменения само изображение). Вся суть хеширования заключается в том, чтобы избежать хранения нескольких версий одного и того же точного изображения на нашем сервере.   -  person 0b10011    schedule 11.05.2012
comment
Да, я понимаю, но так как вы никогда не можете проверить дубликаты прямо сейчас, сравнение хэша загруженного изображения даст вам больше шансов. Во всяком случае, странная проблема. Удачи!   -  person Alix Axel    schedule 11.05.2012
comment
@AlixAxel, о, да, это было бы крайним средством обхода - будем надеяться, что до этого не дойдет! Спасибо! :)   -  person 0b10011    schedule 11.05.2012
comment
Не могли бы вы попробовать заменить все вхождения $cover (после imagejpeg($new_cover, $cover, 100) включительно) на $cover . '.new' и посмотреть, сохраняется ли проблема?   -  person Alix Axel    schedule 12.05.2012
comment
Возможный дубликат вопроса stackoverflow.com / questions / 3614925 /   -  person Bud Damyanov    schedule 16.05.2012
comment
@AlixAxel, браво, проблема была в имени переменной. Не могли бы вы опубликовать это в качестве ответа в течение следующих 20 минут или около того, чтобы я мог наградить награду и принять ее?   -  person 0b10011    schedule 16.05.2012
comment
@AlixAxel, а если нет, не могли бы вы опубликовать свой ответ позже, чтобы вы могли хотя бы получить должное вознаграждение за выяснение проблемы в коде? Спасибо!   -  person 0b10011    schedule 16.05.2012


Ответы (10)


По-видимому, GD не любит, когда путь к файлу ввода / вывода одинаковый, но кредит не моя. Чтобы исправить это, используйте новый файл (tmp), чтобы сохранить вновь созданное изображение в:

<?php
...
if(!imagecopyresampled($new_cover, $original_cover, 0, 0, 0, 0, $new_width, $new_height, $width, $height)){
 return false; // Could not copy image
}
// Create a tmp file.
$cover_new = tempnam('/tmp', 'cover-');
// Use $cover_new instead of $cover
if(!imagejpeg($new_cover, $cover_new, 100)){
 return false; // Image could not be saved to tmp file
}
// Use $cover_new instead of $cover
$file_hash = md5_file($cover_new);
$duplicate_book_cover = $this->find_duplicate_book_cover($book, $file_hash);
if($duplicate_book_cover){
 return $duplicate_book_cover;
}
// Use $cover_new instead of $cover
$file_id = $c_consummo->upload_file($cover_new, $filename);
...
person Alix Axel    schedule 16.05.2012

Похоже, вы пробовали здесь несколько вещей, но давайте попробуем еще. Вам НИКОГДА нужна информация EXIF ​​в вашем приложении? Если нет, давайте уберем поддержку EXIF ​​и посмотрим, удалит ли это полностью.

Если он не удаляет его, то, возможно, функции читают его с существующих фотографий, а затем просто вслепую включают в записываемый файл. Вы можете узнать наверняка, распечатав информацию EXIF ​​на каждом этапе процесса.

person Michael Blood    schedule 11.05.2012
comment
Я распечатал информацию EXIF ​​после каждого шага - она ​​обновляется с текущей датой и временем каждый раз, когда я обновляюсь. И нет, мне не нужна (или не нужна) информация EXIF ​​в моем приложении - как мне отключить для нее поддержку? - person 0b10011; 11.05.2012
comment
Возможно, удалите расширение из php.ini. php.net/manual/en/exif .installation.php - person Michael Blood; 12.05.2012
comment
Оказалось, что проблема является переменной, как упоминалось AlixAxel в комментариях к вопросу. Спасибо за предложение! - person 0b10011; 16.05.2012

Понятия не имею, почему записываются данные EXIF ​​- поэтому следующее может помочь вам удалить их.

Одно из предложений - запустить что-то как команду на сервере - для этого потребуется некоторая установка: http://hacktux.com/read/remove/exif - затем запустить через EXEC из PHP.

Здесь также размещено решение, в котором используется ImageMagick, если он у вас также установлен (или можно установить его: Удалите данные EXIF ​​из JPG с помощью PHP (но обратите внимание на предупреждение о цвете)

В противном случае другой вариант такой же, как указано выше, попробуйте отключить расширение EXIT.

Извините, если они не помогли, но вы просили какие-либо предложения.

person Robbie    schedule 11.05.2012
comment
Спасибо за предложения, я просмотрю их и посмотрю, поможет ли какое-нибудь из них! - person 0b10011; 11.05.2012


Вы могли бы сначала преобразовать jpeg в gif, а затем преобразовать gif обратно в jpeg. Насколько я понимаю, при этом вы уничтожите данные EXIF. Это взлом, но он должен работать.

person pogeybait    schedule 09.05.2012
comment
Я попробую это на всякий случай, но проблема не в данных EXIF, которые ранее были прикреплены к изображениям - сервер фактически прикрепляет новые данные EXIF ​​к изображениям (полностью игнорируя старые данные EXIF). Это действительно странно и, похоже, зависит от сервера, но я не уверен, какие настройки нужно изменить. - person 0b10011; 09.05.2012
comment
Преобразование в gif также приведет к потере качества, так как это всего 256 цветов. - person Flygenring; 17.05.2012

Единственное, что приходит в голову, это то, что вы можете игнорировать данные EXIF ​​и просто сделать свой хеш из чего-то другого? Мое предложение состояло в том, чтобы попробовать либо хешировать необработанный вывод без сохранения в файл (здесь вывод в формате gif - который делает 8-битное изображение меньшего размера для производительности - в выходной буфер и хеширует содержимое буфера)

if(!imagecopyresampled(...)) {...}
ob_start();
imagegif($new_cover);
$file_hash = md5(ob_get_contents());
ob_end_clean();
if(!imagejpeg($new_cover, $cover, 100)) {...}

Или вы можете создать строку, содержащую только информацию о пикселях и хэш (здесь это делается путем доступа к каждому пикселю и добавления его значения к строке, в обратном порядке для производительности)

$pixels = '';
for($x=$new_width-1; $x>=0; $x--) {
  for($y=$new_height-1; $y>=0; $y--) {
    $pixels .= imagecolorat($new_cover, $x, $y);
  }
}
$file_hash = md5($pixels);

Для повышения производительности вы также можете выбрать только выборку из изображения, так как это должно работать так же хорошо или, может быть, даже лучше (здесь выборка каждого 5-го пикселя каждой 5-й строки)

$pixels = '';
for($x=$new_width-1; $x>=0; $x-=5) {
  for($y=$new_height-1; $y>=0; $y-=5) {
    $pixels .= imagecolorat($new_cover, $x, $y);
  }
}
$file_hash = md5($pixels);

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

person Flygenring    schedule 16.05.2012
comment
Отличная идея, но с ее помощью не удалось решить эту проблему. Но обязательно запомню это на будущее. В качестве примечания: опубликованный вами код был немного поврежден - я отредактирую его через минуту с исправленной версией. - person 0b10011; 16.05.2012
comment
Я наградил вас наградой, поскольку человек, который помог решил проблему, не смог вовремя получить ответ. Кроме того, ваш ответ мыслился нестандартно, предоставлял способ игнорировать данные EXIF ​​(без их удаления) и работал без необходимости перенастраивать сервер. Это также заставило меня понять, что происходит что-то еще, кроме данных EXIF, что, в свою очередь, указывало мне на ответ, опубликованный в комментариях к моему вопросу. Так что спасибо тебе! - person 0b10011; 16.05.2012
comment
Я рад, что вам понравились мои предложения :) Это то, что я обычно делаю, если сталкиваюсь с проблемой, которая работает не так, как должно, в соответствии с документацией - попробуйте найти другое решение, которое не требует этой части это не работает. Мне очень жаль, что правильное решение реальной проблемы не было опубликовано вовремя, но спасибо за награду! Также было бы неплохо сделать комментарий об этом в документации PHP для imagejpeg ()? Причина, по которой я запустил цикл во втором коде в обратном направлении, была связана исключительно с производительностью, но я забыл вычесть единицу. Я просто поправлю это :) - person Flygenring; 17.05.2012

Это немного похоже на взлом, но он сработает, просто сделайте это после вашего switch() оператора:

$original_cover = imagerotate($original_cover,360,0);

GD удалит любые данные EXIF, так как он их не поддерживает.

person Dave    schedule 16.05.2012
comment
Это не работает, извините! (И я не думал, что GD поддерживает это, отсюда и мое замешательство.) - person 0b10011; 16.05.2012
comment
Может нужно сделать это с $ new_cover и $ cover? Может, стоит попробовать. Думаю, я неправильно понял ваш вопрос, когда объяснял, куда его поставить. - person Dave; 16.05.2012
comment
Это странно! У вас есть доступ к программе преобразования ImageMagick на сервере? Я думаю, это ваш лучший выбор. - person Dave; 16.05.2012
comment
Я знаю! И да, я думаю, это то, к чему я должен прибегнуть. Надеюсь, однако, на другое решение. - person 0b10011; 16.05.2012

Вы можете попробовать использовать imagecreatefromjpeg на изображении, созданном с помощью imagejpeg, и заменить его новым.

person Newton Smartt    schedule 09.05.2012

$res = imagecreatefromjpeg($filename), чтобы загрузить изображение, затем imagejpeg($res, $filename, QUALITY), чтобы повторно визуализировать его.

Вы также можете использовать imagemagick:

$img = new Imagick($image);
    $img->stripImage();
    $img->writeImage($image);
    $img->clear();
    $img->destroy();
person Moritz    schedule 09.05.2012

Ваш PHP должен быть скомпилирован с --enable-exif.

Попробуйте отключить глобальную функциональность EXIF, перекомпилировав PHP без этой опции.

person Bud Damyanov    schedule 16.05.2012