Вопрос или проблема
`
<?php
error_reporting(E_ALL);
ini_set('display_errors', 1);
define('RESPONSE_PREVIEW_LENGTH', 1000);
class HttpErrorException extends Exception {}
class JsonDecodeException extends Exception {}
class RequestHandler
{
private string $url;
private array $data;
private ?int $httpCode = null;
private ?string $responseArray = null;
public function __construct(string $url, array $data)
{
$this->url = $url;
$this->data = $data;
}
public function makeAndDisplayRequest(): string
{
$curlHandle = curl_init();
try {
// Отладка подготовленного URL
$url = $this->prepareUrl();
$this->logToFileConnectionInfo('URL: ' . print_r($url, true)); // Логируем URL и детали подключения
// Отладка обработчика curl
$this->configureCurl($curlHandle, $url);
$this->logToFileConnectionInfo('cURL Обработчик: ' . base64_encode(print_r($curlHandle, true)));
$this->logRequestDetails($url);
// Отладка массива ответа от curl_exec
$this->responseArray = curl_exec($curlHandle);
$this->logToFileConnectionInfo('Массив ответа: ' . base64_encode(print_r($this->responseArray, true))); // Логируем ответ
// Проверка на ошибки выполнения cURL
if ($this->responseArray === false) {
throw new Exception('Ошибка выполнения cURL: ' . curl_error($curlHandle));
}
// Получить HTTP статус код
$this->httpCode = curl_getinfo($curlHandle, CURLINFO_HTTP_CODE);
$this->logToFileConnectionInfo('HTTP Код: ' . $this->httpCode); // Логируем HTTP код
// Проверка, является ли ответ изображением (например, image/jpeg или image/png)
$contentType = curl_getinfo($curlHandle, CURLINFO_CONTENT_TYPE);
if (preg_match('/image\/(jpeg|png)/', $contentType)) {
// Если это изображение, сохранить изображение в файл
$this->logToFileConnectionInfo('Обнаружен контент изображения: ' . $contentType);
$this->saveImageToFile($this->responseArray); // Сохраняем изображение в файл
// Извлечь и сохранить EXIF метаданные из изображения, если они доступны
$this->extractAndSaveExifData($this->responseArray);
return "Контент изображения сохранён.";
}
// Отладка обработки ошибок curl
$this->handleCurlErrors($curlHandle);
// Отладка обработки HTTP ошибок
$this->handleHttpErrors();
// Отладка обработанного ответа
$processedResponse = $this->processResponse($this->responseArray);
$this->logToFileConnectionInfo('Обработанный ответ: ' . base64_encode(print_r($processedResponse, true))); // Логируем обработанный ответ
return $processedResponse; // Возвращаем обработанный ответ
} catch (HttpErrorException | JsonDecodeException | Exception $ex) {
// Логируем сообщение об исключении
$this->logToFileConnectionInfo('ОШИБКА - ' . $ex->getMessage());
echo "ОШИБКА - " . $ex->getMessage() . PHP_EOL;
return "";
} finally {
curl_close($curlHandle);
}
}
/**
* Сохранить контент изображения в файл.
*
* @param string $imageData Сырые бинарные данные изображения.
*/
private function saveImageToFile(string $imageData): void
{
// Определите путь к файлу, где будет сохранено изображение
$imagePath="C:\\Users\\Pavel\\Desktop\\testy na pátek\\image.jpg"; // Измените имя файла и расширение при необходимости
file_put_contents($imagePath, $imageData); // Записать данные изображения в файл
$this->logToFileImage('Изображение сохранено в: ' . $imagePath); // Логируем детали сохранения изображения
}
/**
* Извлечь и сохранить EXIF метаданные из изображения.
*
* @param string $imageData Сырые бинарные данные изображения.
*/
private function extractAndSaveExifData(string $imageData): void
{
// Временно сохранить данные изображения в файл, чтобы извлечь EXIF данные
$tempImagePath="C:\\Users\\Pavel\\Desktop\\testy na pátek\\temp_image.jpg";
file_put_contents($tempImagePath, $imageData);
// Попробуйте извлечь EXIF данные из изображения
$exifData = @exif_read_data($tempImagePath);
if ($exifData !== false) {
// Извлечь и залогировать EXIF данные
$this->logExifData('EXIF Данные: ', $exifData);
} else {
$this->logToFileExif('EXIF данные не найдены.');
}
// Попробуйте извлечь данные IPTC из изображения (маркер APP13)
$iptcData = @getimagesize($tempImagePath, $info);
if (isset($info['APP13'])) {
$this->logToFileExif('Данные IPTC найдены!');
// При необходимости обработать данные IPTC
} else {
$this->logToFileExif('Данные IPTC не найдены.');
}
// Попробуйте извлечь данные XMP (из маркера APP1)
if (isset($info['APP1'])) {
$xmpData = $info['APP1'];
if (strpos($xmpData, 'XMP') !== false) {
$this->logToFileExif('Данные XMP найдены!');
} else {
$this->logToFileExif('Данные XMP не найдены.');
}
} else {
$this->logToFileExif('Данные XMP не найдены.');
}
// Проверяем наличие ICC профиля (маркер APP2)
if (isset($info['APP2'])) {
$this->logToFileExif('ICC Профиль найден!');
} else {
$this->logToFileExif('ICC Профиль не найден.');
}
// Удалить временный файл изображения
unlink($tempImagePath);
}
/**
* Логировать EXIF метаданные в отдельный файл.
*
* @param string $message Сообщение для логирования.
* @param array $data Массив EXIF данных.
*/
private function logExifData(string $message, array $data): void
{
// Логируем начальное сообщение
$this->logToFileExif($message);
// Логируем все EXIF данные
$this->logToFileExif("EXIF Данные: " . print_r($data, true));
// Логируем конкретные теги EXIF структурированным образом
$this->logToFileExif("Информация о файле:");
$this->logToFileExif(" Имя файла: " . ($data['FileName'] ?? 'Недоступно'));
$this->logToFileExif(" Размер файла: " . ($data['FileSize'] ?? 'Недоступно'));
$this->logToFileExif(" Тип файла: " . ($data['FileType'] ?? 'Недоступно'));
$this->logToFileExif(" MimeType: " . ($data['MimeType'] ?? 'Недоступно'));
$this->logToFileExif(" Дата и время файла: " . (isset($data['FileDateTime']) ? date('Y-m-d H:i:s', $data['FileDateTime']) : 'Недоступно'));
$this->logToFileExif("Информация об изображении:");
$this->logToFileExif(" Ширина: " . ($data['COMPUTED']['Width'] ?? 'Недоступно'));
$this->logToFileExif(" Высота: " . ($data['COMPUTED']['Height'] ?? 'Недоступно'));
$this->logToFileExif("Информация о камере:");
$this->logToFileExif(" Производитель: " . ($data['Make'] ?? 'Недоступно'));
$this->logToFileExif(" Модель: " . ($data['Model'] ?? 'Недоступно'));
$this->logToFileExif(" Фокусное расстояние: " . ($data['FocalLength'] ?? 'Недоступно'));
$this->logToFileExif(" Ориентация: " . ($data['Orientation'] ?? 'Недоступно'));
$this->logToFileExif(" Дата оригинала: " . ($data['DateTimeOriginal'] ?? 'Недоступно'));
$this->logToFileExif(" Дата тиражирования: " . ($data['DateTimeDigitized'] ?? 'Недоступно'));
$this->logToFileExif(" Источник света: " . ($data['LightSource'] ?? 'Недоступно'));
// Логируем GPS данные если они доступны
if (isset($data['GPSLatitude']) && isset($data['GPSLongitude'])) {
$latitude = $this->convertGpsToDecimal($data['GPSLatitude'], $data['GPSLatitudeRef']);
$longitude = $this->convertGpsToDecimal($data['GPSLongitude'], $data['GPSLongitudeRef']);
$this->logToFileExif("GPS Координаты: Широта " . $latitude . ", Долгота " . $longitude);
$this->logToFileExif(" ШиротаRef: " . ($data['GPSLatitudeRef'] ?? 'Недоступно'));
$this->logToFileExif(" ДолготаRef: " . ($data['GPSLongitudeRef'] ?? 'Недоступно'));
$this->logToFileExif(" Высота: " . ($data['GPSAltitude'] ?? 'Недоступно'));
$this->logToFileExif(" ВысотаRef: " . ($data['GPSAltitudeRef'] ?? 'Недоступно'));
$this->logToFileExif(" Направление: " . ($data['GPSImgDirection'] ?? 'Недоступно'));
$this->logToFileExif(" Спутники: " . ($data['GPSSatellites'] ?? 'Недоступно'));
$this->logToFileExif(" DOP: " . ($data['GPSDOP'] ?? 'Недоступно'));
} else {
$this->logToFileExif("GPS Координаты: Недоступно");
}
// Логируем комментарии EXIF и информацию об авторе
$this->logToFileExif("EXIF Комментарий: " . ($data['COMMENT'][0] ?? 'Недоступно'));
$this->logToFileExif("Автор: " . ($data['Artist'] ?? 'Недоступно'));
// Логируем любые дополнительные EXIF данные динамически, даже если вы не знаете точные названия ключей
$this->logToFileExif("Другие EXIF Данные:");
foreach ($data as $key => $value) {
// Пропускаем известные ключи, чтобы избежать дублирования
if (in_array($key, ['FileName', 'FileSize', 'FileType', 'MimeType', 'FileDateTime', 'COMMENT', 'Artist'])) {
continue;
}
// Логируем все оставшиеся EXIF ключи и значения
$this->logToFileExif(" $key: " . (is_array($value) ? print_r($value, true) : $value));
}
}
/**
* Преобразовать GPS данные (Широта/Долгота) в десятичный формат.
*
* @param array $gpsData Массив GPS данных (например, [градусы, минуты, секунды]).
* @param string $ref Направление (С/Ю или В/З).
* @return float Десятичная GPS координата.
*/
private function convertGpsToDecimal(array $gpsData, string $ref): float
{
var_dump($gpsData); // Логируем GPS данные для отладки
// Вспомогательная функция для разбора строк дробей, таких как "49/1"
// Это следует переместить за пределы этой функции, чтобы избежать проблем с повторным объявлением
if (!function_exists('parseFraction')) {
function parseFraction($fraction) {
// Разделить строку на числитель и знаменатель
$parts = explode("https://stackoverflow.com/", $fraction);
// Вернуть результат деления (если обе части существуют и знаменатель не равен нулю)
return isset($parts[0], $parts[1]) && $parts[1] != 0 ? $parts[0] / $parts[1] : 0;
}
}
// Проверяем, являются ли GPS данные в ожидаемом формате и обрабатываем случаи, когда некоторые значения могут отсутствовать
if (count($gpsData) < 2) {
// Обработка случая, если данные неполные (например, отсутствие широты или долготы)
$this->logToFileExif("Неполные GPS данные: " . print_r($gpsData, true));
return 0.0; // Вернуть значение по умолчанию (или обработать как ошибку)
}
// Преобразование GPS данных (широта, долгота, высота) из массивов дробей
$degrees = isset($gpsData[0], $gpsData[1]) ? parseFraction($gpsData[0]) : 0; // Избежать деления на ноль
$minutes = isset($gpsData[2], $gpsData[3]) ? parseFraction($gpsData[2]) : 0; // Избежать деления на ноль
$seconds = isset($gpsData[4], $gpsData[5]) ? parseFraction($gpsData[4]) : 0; // Избежать деления на ноль
// Суммируем, чтобы получить десятичный формат
$decimal = $degrees + ($minutes / 60) + ($seconds / 3600);
// Корректируем в зависимости от направления (С/Ю/В/З)
if ($ref === 'S' || $ref === 'W') {
$decimal *= -1;
}
return $decimal;
}
/**
* Логировать EXIF метаданные в отдельный файл.
*
* @param string $data EXIF данные для логирования.
*/
private function logToFileExif(string $data): void
{
$logFile = "C:\\Users\\Pavel\\Desktop\\testy na pátek\\exif_log.txt"; // Укажите путь к вашему файлу логирования метаданных EXIF
$timestamp = date('[Y-m-d H:i:s]'); // Добавьте метку времени
file_put_contents($logFile, $timestamp . ' ' . $data . PHP_EOL, FILE_APPEND);
}
/**
* Логировать детали подключения и ответа сервера в файл.
*
* @param string $data Данные для логирования.
*/
private function logToFileConnectionInfo(string $data): void
{
$logFile = "C:\\Users\\Pavel\\Desktop\\testy na pátek\\connection_response_log.txt"; // Укажите путь к вашему файлу лога соединения и ответа
$timestamp = date('[Y-m-d H:i:s]'); // Добавьте метку времени
file_put_contents($logFile, $timestamp . ' ' . $data . PHP_EOL, FILE_APPEND);
}
/**
* Логировать детали, связанные с изображением в отдельный файл.
*
* @param string $data Данные, связанные с изображением, для логирования.
*/
private function logToFileImage(string $data): void
{
$logFile = "C:\\Users\\Pavel\\Desktop\\testy na pátek\\image_log.txt"; // Укажите путь к вашему файлу лога изображений
$timestamp = date('[Y-m-d H:i:s]'); // Добавьте метку времени
file_put_contents($logFile, $timestamp . ' ' . $data . PHP_EOL, FILE_APPEND);
}
`
Этот код предназначен для отправки запроса curl и работы с данными, полученными в ответ. Поскольку ответ содержит изображение и многие другие данные, я решил разделить выводы.
В настоящее время я получаю:
[2024-11-08 13:34:40] EXIF Данные:
[2024-11-08 13:34:40] EXIF Данные: Массив
(
[FileName] => temp_image.jpg
[FileDateTime] => 1731069280
[FileSize] => 174745
[FileType] => 2
[MimeType] => image/jpeg
[SectionsFound] => ANY_TAG, IFD0, COMMENT, EXIF, GPS
[COMPUTED] => Массив
(
[html] => width=”1200″ height=”900″
[Height] => 900
[Width] => 1200
[IsColor] => 1
[ByteOrderMotorola] => 0
)[Artist] => c076e10a-f802-4072-abe8-e1d7fd4079d5
[ImageWidth] => 4000
[Model] => SM-A336B
[ImageLength] => 3000
[Make] => samsung
[Orientation] => 6
[DateTime] => 2024:10:11 08:02:04
[Exif_IFD_Pointer] => 196
[GPS_IFD_Pointer] => 298
[COMMENT] => Массив
(
[0] => CREATOR: gd-jpeg v1.0 (используя IJG JPEG v62), качество по умолчанию)
[DateTimeOriginal] => 2024:10:11 08:02:04
[DateTimeDigitized] => 2024:10:11 08:02:04
[FocalLength] => 93/20
[LightSource] => 0
[GPSLatitude] => Массив
(
[0] => 49/1
[1] => 12/1
[2] => 8602/223
)[GPSAltitude] => 1453/5
[GPSLatitudeRef] => N
[GPSAltitudeRef] =>
[GPSLongitudeRef] => E
[GPSSatellites] => {“totalCount”:”0″,”usedCount”:”0″}
[GPSLongitude] => Массив
(
[0] => 16/1
[1] => 35/1
[2] => 6038/185
)[GPSImgDirection] => 187091/555
[GPSDOP] => 7/5
[GPSImgDirectionRef] => M
)[2024-11-08 13:34:40] Информация о файле:
[2024-11-08 13:34:40] Имя файла: temp_image.jpg
[2024-11-08 13:34:40] Размер файла: 174745
[2024-11-08 13:34:40] Тип файла: 2
[2024-11-08 13:34:40] MimeType: image/jpeg
[2024-11-08 13:34:40] Дата и время файла: 2024-11-08 13:34:40
[2024-11-08 13:34:40] Информация об изображении:
[2024-11-08 13:34:40] Ширина: 1200
[2024-11-08 13:34:40] Высота: 900
[2024-11-08 13:34:40] Информация о камере:
[2024-11-08 13:34:40] Производитель: samsung
[2024-11-08 13:34:40] Модель: SM-A336B
[2024-11-08 13:34:40] Фокусное расстояние: 93/20
[2024-11-08 13:34:40] Ориентация: 6
[2024-11-08 13:34:40] Дата оригинала: 2024:10:11 08:02:04
[2024-11-08 13:34:40] Дата тиражирования: 2024:10:11 08:02:04
[2024-11-08 13:34:40] Источник света: 0
[2024-11-08 13:34:40] GPS Координаты: Широта 49, Долгота 16
[2024-11-08 13:34:40] ШиротаRef: N
[2024-11-08 13:34:40] ДолготаRef: E
[2024-11-08 13:34:40] Высота: 1453/5
[2024-11-08 13:34:40] ВысотаRef:
[2024-11-08 13:34:40] Направление: 187091/555
[2024-11-08 13:34:40] Спутники: {“totalCount”:”0″,”usedCount”:”0″}
[2024-11-08 13:34:40] DOP: 7/5
[2024-11-08 13:34:40] EXIF Комментарий: CREATOR: gd-jpeg v1.0 (используя IJG JPEG v62), качество по умолчанию[2024-11-08 13:34:40] Автор: c076e10a-f802-4072-abe8-e1d7fd4079d5
[2024-11-08 13:34:40] Другие EXIF Данные:
[2024-11-08 13:34:40] SectionsFound: ANY_TAG, IFD0, COMMENT, EXIF, GPS
[2024-11-08 13:34:40] COMPUTED: Массив
(
[html] => width=”1200″ height=”900″
[Height] => 900
[Width] => 1200
[IsColor] => 1
[ByteOrderMotorola] => 0
)[2024-11-08 13:34:40] ImageWidth: 4000
[2024-11-08 13:34:40] Model: SM-A336B
[2024-11-08 13:34:40] ImageLength: 3000
[2024-11-08 13:34:40] Make: samsung
[2024-11-08 13:34:40] Orientation: 6
[2024-11-08 13:34:40] DateTime: 2024:10:11 08:02:04
[2024-11-08 13:34:40] Exif_IFD_Pointer: 196
[2024-11-08 13:34:40] GPS_IFD_Pointer: 298
[2024-11-08 13:34:40] DateTimeOriginal: 2024:10:11 08:02:04
[2024-11-08 13:34:40] DateTimeDigitized: 2024:10:11 08:02:04
[2024-11-08 13:34:40] FocalLength: 93/20
[2024-11-08 13:34:40] LightSource: 0
[2024-11-08 13:34:40] GPSLatitude: Массив
(
[0] => 49/1
[1] => 12/1
[2] => 8602/223
)[2024-11-08 13:34:40] GPSAltitude: 1453/5
[2024-11-08 13:34:40] GPSLatitudeRef: N
[2024-11-08 13:34:40] GPSAltitudeRef:
[2024-11-08 13:34:40] GPSLongitudeRef: E
[2024-11-08 13:34:40] GPSSatellites: {“totalCount”:”0″,”usedCount”:”0″}
[2024-11-08 13:34:40] GPSLongitude: Массив
(
[0] => 16/1
[1] => 35/1
[2] => 6038/185
)[2024-11-08 13:34:40] GPSImgDirection: 187091/555
[2024-11-08 13:34:40] GPSDOP: 7/5
[2024-11-08 13:34:40] GPSImgDirectionRef: M
[2024-11-08 13:34:40] Данные IPTC не найдены.
[2024-11-08 13:34:40] Данные XMP не найдены.
[2024-11-08 13:34:40] ICC Профиль найден!
Но я абсолютно уверен, что эти данные не все, но все еще я не уверен, как получить их все. Что делать, чтобы убедиться, что я получил каждую деталь, возможно, если мне нужно даже улучшить обработку запросов curl или кодирование изображения + ответа.
Ссылка на файл .txt с массивом ответа: https://file.io/h2COV3XQaGSk
Я пытался использовать curl + разделение ответа, что, кажется, почти нормально, но все же мне нужно убедиться, что я не пропустил ни одной детали для сервера, интерфейса и тестирования файлов изображений, где мне нужно убедиться, что все работает так, как должно, и если нет, я получу ответ об ошибке или исключение.
Ответ или решение
Обработка EXIF и метаданных из ответа сервера, содержащего изображение
В данном ответе мы рассмотрим, как можно улучшить обработку EXIF и метаданных ответов сервера, содержащих изображения, с использованием предоставленного кода на PHP. Этот код включает функции для отправки HTTP-запросов и анализа получаемых изображений. Мы сосредоточимся на том, как улучшить функциональность, чтобы обеспечить максимальное извлечение информации и надежность обработки.
1. Улучшение запроса cURL
Для начала, убедитесь, что вы используете cURL правильно для получения изображений и других данных. Настройки cURL, такие как тайм-ауты и обработка редиректов, могут помочь вам избежать ошибок, которые могут возникнуть из-за сетевых проблем. Рекомендуется добавить:
curl_setopt($curlHandle, CURLOPT_TIMEOUT, 30); // Установка тайм-аута
curl_setopt($curlHandle, CURLOPT_FOLLOWLOCATION, true); // Автоматическое выполнение редиректов
Это обеспечит более устойчивую работу вашего кода при получении ответов от сервера.
2. Подробный анализ EXIF и других метаданных
Хотя EXIF предоставляет множество полезной информации, иногда дополнительные метаданные могут быть недоступны или недостаточно извлечены. Для этого рассмотрим несколько методов:
-
IPTC и XMP метаданные: Вы правильно пытаетесь извлечь IPTC и XMP данные, но важно убедиться, что вы обладаете правильными функциями для их извлечения. Ваша реализация для IPTC выглядит должным образом, проверяйте также наличие XMP метаданных в другом коде, используя специализированные библиотеки PHP, такие как
php-exif
илиImageMagick
. -
Проверка других форматов изображений: Помимо JPEG и PNG, могут быть другие форматы, которые ваше приложение может обрабатывать, такие как TIFF или GIF. Убедитесь, что обработка EXIF затрагивает все возможные форматы, где это возможно.
3. Логиирование
Эффективное логирование информации облегчит диагностику и улучшит поддержку приложения. Убедитесь, что вы записываете:
- Полные ответы сервера: Логируйте не только заголовки, но и всю структуру ответа, что может помочь в анализе ошибок.
- Документирование ошибок: Каждый тип ошибки (cURL, HTTP и внутренние ошибки) должно логироваться в разные файлы или с разными префиксами для удобства.
private function logError(string $errorMessage): void {
error_log(date('[Y-m-d H:i:s] ') . $errorMessage . PHP_EOL, 3, "error_log.txt");
}
4. Исключение потенциальных ошибок
Для улучшения устойчивости стоит добавить больше проверки возможных ошибок. Например, если EXIF данные не извлекаются, вы должны обрабатывать это, возвращая пользователю доступное сообщение об ошибке:
if ($exifData === false) {
throw new Exception('Не удалось извлечь EXIF данные из изображения.');
}
5. Параллельная обработка
Если ваше приложение должно обрабатывать множество изображений одновременно, рассмотрите использование параллельной обработки через cURL с использованием curl_multi_exec
. Это не только ускорит процесс, но и повысит масштабируемость вашего решения.
6. Улучшение информации о GPS
GPS данные могут содержать дополнительную информацию, которую стоит анализировать более глубоко. Убедитесь, что вы проверяете всю доступную информацию и обрабатываете возможные исключения:
if (!empty($data['GPSLatitude'])) {
// Подробная проверка и преобразование GPS
} else {
$this->logToFileExif(' GPS данные отсутствуют.');
}
Заключение
Эти улучшения помогут вам комплексно обработать EXIF и другие метаданные изображений, приходящих с серверов. Таблица обработки ошибок, улучшенное логирование, дополнительно улучшенная обработка других форматов и параллельная обработка помогут вашему приложению стать более надежным и эффективным.
Убедитесь, что вы проанализировали и протестировали код, чтобы избежать потенциальных ошибок в будущем, и всегда оставайтесь в курсе новых методов обработки изображений и метаданных.