Skip to content

Exif 信息读取

PHP 的 Exif 扩展用于读取 JPEG 和 TIFF 图像文件中的 EXIF 元数据。EXIF 数据由数码相机拍摄时自动嵌入,包含拍摄时间、相机型号、光圈、快门速度、GPS 坐标等信息,广泛应用于照片管理和地理标记。

前置知识

阅读本节前,建议先了解:GD 图像处理

基础概念

安装

bash
# 编译安装
./configure --enable-exif

# Ubuntu/Debian
sudo apt-get install php-exif

// php.ini
// exif.encode_unicode = ISO-8859-15
// exif.decode_unicode_motorola = UCS-2BE
// exif.decode_unicode_intel    = UCS-2LE
// exif.decode_jis_motorola     = JIS
// exif.decode_jis_intel        = JIS

读取 EXIF 数据

基本读取

php
<?php
declare(strict_types=1);

/**
 * 读取 JPEG 文件的 EXIF 信息
 */
function readExif(string $filePath): array
{
    if (!file_exists($filePath)) {
        throw new RuntimeException("文件不存在: {$filePath}");
    }

    // exif_read_data 读取 EXIF
    $exif = @exif_read_data($filePath, 'ANY_TAG', true);

    if ($exif === false) {
        throw new RuntimeException("无法读取 EXIF 数据");
    }

    return $exif;
}

// 使用示例
// $exif = readExif('/path/to/photo.jpg');
// print_r($exif);

常用 EXIF 字段

php
<?php
declare(strict_types=1);

function getPhotoInfo(string $filePath): array
{
    $exif = @exif_read_data($filePath, 'ANY_TAG', true);
    if ($exif === false) {
        return [];
    }

    return [
        // 文件信息
        'file_name'    => basename($filePath),
        'file_type'    => $exif['FILE']['MimeType'] ?? '',
        'file_size'    => filesize($filePath),
        'file_width'   => $exif['COMPUTED']['Width'] ?? 0,
        'file_height'  => $exif['COMPUTED']['Height'] ?? 0,

        // 拍摄信息
        'camera'       => ($exif['IFD0']['Make'] ?? '') . ' ' . ($exif['IFD0']['Model'] ?? ''),
        'date_time'    => $exif['EXIF']['DateTimeOriginal'] ?? ($exif['IFD0']['DateTime'] ?? ''),

        // 拍摄参数
        'exposure'     => $exif['EXIF']['ExposureTime'] ?? '',
        'f_number'     => $exif['COMPUTED']['ApertureFNumber'] ?? '',
        'iso'          => $exif['EXIF']['ISOSpeedRatings'] ?? '',
        'focal_length' => $exif['EXIF']['FocalLength'] ?? '',
        'shutter'      => $exif['EXIF']['ExposureTime'] ?? '',

        // 方向
        'orientation'  => $exif['IFD0']['Orientation'] ?? 1,

        // 软件
        'software'     => $exif['IFD0']['Software'] ?? '',

        // GPS
        'gps_latitude'  => $exif['GPS']['GPSLatitude'] ?? null,
        'gps_longitude' => $exif['GPS']['GPSLongitude'] ?? null,
    ];
}

// 使用示例
// $info = getPhotoInfo('/path/to/photo.jpg');
// echo "相机: {$info['camera']}" . PHP_EOL;
// echo "拍摄时间: {$info['date_time']}" . PHP_EOL;
// echo "ISO: {$info['iso']}" . PHP_EOL;

GPS 坐标提取

php
<?php
declare(strict_types=1);

/**
 * 从 EXIF GPS 数据中提取经纬度
 */
function getGpsCoordinates(string $filePath): ?array
{
    $exif = @exif_read_data($filePath, 'ANY_TAG', true);

    if (!isset($exif['GPS']['GPSLatitude'], $exif['GPS']['GPSLongitude'])) {
        return null;
    }

    $latDms = $exif['GPS']['GPSLatitude'];
    $lngDms = $exif['GPS']['GPSLongitude'];
    $latRef = $exif['GPS']['GPSLatitudeRef'] ?? 'N';
    $lngRef = $exif['GPS']['GPSLongitudeRef'] ?? 'E';

    // 转换 DMS(度分秒)为十进制
    $latitude = $this->dmsToDecimal($latDms[0], $latDms[1], $latDms[2]);
    $longitude = $this->dmsToDecimal($lngDms[0], $lngDms[1], $lngDms[2]);

    if ($latRef === 'S') {
        $latitude = -$latitude;
    }
    if ($lngRef === 'W') {
        $longitude = -$longitude;
    }

    return [
        'latitude'  => $latitude,
        'longitude' => $longitude,
    ];
}

function dmsToDecimal(float $degrees, float $minutes, float $seconds): float
{
    return $degrees + ($minutes / 60) + ($seconds / 3600);
}

// 使用示例
// $coords = getGpsCoordinates('/path/to/photo.jpg');
// if ($coords !== null) {
//     echo "纬度: {$coords['latitude']}" . PHP_EOL;
//     echo "经度: {$coords['longitude']}" . PHP_EOL;
// }

图像方向修正

php
<?php
declare(strict_types=1);

/**
 * 根据 EXIF 方向信息旋转图片
 */
function fixOrientation(string $filePath, string $outputPath): void
{
    $exif = @exif_read_data($filePath);
    if ($exif === false) {
        return;
    }

    $orientation = $exif['IFD0']['Orientation'] ?? 1;

    $image = imagecreatefromjpeg($filePath);

    match ($orientation) {
        3 => imagerotate($image, 180, 0),
        6 => imagerotate($image, -90, 0),
        8 => imagerotate($image, 90, 0),
        default => null,
    };

    imagejpeg($image, $outputPath, 90);
    imagedestroy($image);
}

注意事项

  • exif_read_data() 仅支持 JPEG 和 TIFF 格式
  • 需要 mbstring 扩展处理 Unicode 字符
  • 读取用户上传的 EXIF 数据时注意隐私保护

下一节

继续学习:intl 扩展

参考链接