本文目录导读:

我来为您详细介绍PHP项目中读取EXIF和元数据的方法。
基本EXIF读取(使用PHP内置函数)
<?php
// 检查EXIF扩展是否可用
if (function_exists('exif_read_data')) {
$imagePath = 'photo.jpg';
// 读取EXIF数据
$exif = exif_read_data($imagePath, 'ANY_TAG', true);
if ($exif !== false) {
echo "相机型号: " . ($exif['IFD0']['Model'] ?? '未知');
echo "拍摄时间: " . ($exif['EXIF']['DateTimeOriginal'] ?? '未知');
echo "ISO: " . ($exif['EXIF']['ISOSpeedRatings'] ?? '未知');
echo "焦距: " . ($exif['EXIF']['FocalLength'] ?? '未知');
echo "光圈: " . ($exif['EXIF']['FNumber'] ?? '未知');
echo "快门速度: " . ($exif['EXIF']['ExposureTime'] ?? '未知');
}
} else {
echo "需要启用PHP EXIF扩展";
}
?>
完整的EXIF读取类
<?php
class ExifReader
{
private $imagePath;
private $exifData;
public function __construct($imagePath)
{
$this->imagePath = $imagePath;
$this->loadExif();
}
private function loadExif()
{
if (!file_exists($this->imagePath)) {
throw new Exception("文件不存在");
}
if (!function_exists('exif_read_data')) {
throw new Exception("EXIF扩展未启用");
}
$this->exifData = @exif_read_data($this->imagePath, 'ANY_TAG', true);
if ($this->exifData === false) {
throw new Exception("无法读取EXIF数据");
}
}
// 获取拍摄信息
public function getCameraInfo()
{
$info = [];
if (isset($this->exifData['IFD0'])) {
$info = [
'make' => $this->exifData['IFD0']['Make'] ?? '未知',
'model' => $this->exifData['IFD0']['Model'] ?? '未知',
'software' => $this->exifData['IFD0']['Software'] ?? '未知'
];
}
return $info;
}
// 获取拍摄参数
public function getCameraSettings()
{
$settings = [];
if (isset($this->exifData['EXIF'])) {
$settings = [
'iso' => $this->exifData['EXIF']['ISOSpeedRatings'] ?? '未知',
'focal_length' => $this->exifData['EXIF']['FocalLength'] ?? '未知',
'f_number' => $this->exifData['EXIF']['FNumber'] ?? '未知',
'exposure_time' => $this->exifData['EXIF']['ExposureTime'] ?? '未知',
'flash' => $this->getFlashStatus(),
'white_balance' => $this->getWhiteBalance()
];
}
return $settings;
}
// 获取GPS信息
public function getGPSInfo()
{
$gps = [];
if (isset($this->exifData['GPS'])) {
$latitude = $this->getGPSValue('GPSLatitude', 'GPSLatitudeRef');
$longitude = $this->getGPSValue('GPSLongitude', 'GPSLongitudeRef');
$altitude = $this->exifData['GPS']['GPSAltitude'] ?? null;
$gps = [
'latitude' => $latitude,
'longitude' => $longitude,
'altitude' => $altitude,
'latitude_decimal' => $this->convertDMSToDecimal($latitude),
'longitude_decimal' => $this->convertDMSToDecimal($longitude)
];
}
return $gps;
}
// 获取日期时间信息
public function getDateTimeInfo()
{
$dates = [];
if (isset($this->exifData['EXIF']['DateTimeOriginal'])) {
$dates['original'] = $this->exifData['EXIF']['DateTimeOriginal'];
$dates['digitized'] = $this->exifData['EXIF']['DateTimeDigitized'] ?? null;
}
return $dates;
}
// 获取图像信息
public function getImageInfo()
{
$info = [];
if (isset($this->exifData['COMPUTED'])) {
$info = [
'width' => $this->exifData['COMPUTED']['Width'] ?? 0,
'height' => $this->exifData['COMPUTED']['Height'] ?? 0,
'orientation' => $this->getOrientation(),
'color_space' => $this->exifData['EXIF']['ColorSpace'] ?? '未知'
];
}
return $info;
}
// 辅助方法:获取Flash状态
private function getFlashStatus()
{
$flash = $this->exifData['EXIF']['Flash'] ?? 0;
$flashStatus = [
0 => '未使用闪光灯',
1 => '使用闪光灯',
5 => '使用闪光灯(防红眼)',
7 => '使用闪光灯(强制闪光)',
9 => '使用闪光灯(慢同步)',
16 => '未使用闪光灯(强制关闭)',
24 => '自动模式,未使用闪光灯',
25 => '自动模式,使用闪光灯'
];
return $flashStatus[$flash] ?? '未知 (' . $flash . ')';
}
// 辅助方法:获取白平衡
private function getWhiteBalance()
{
$wb = $this->exifData['EXIF']['WhiteBalance'] ?? 0;
return $wb == 0 ? '自动' : '手动';
}
// 辅助方法:获取方向
private function getOrientation()
{
$orientation = $this->exifData['IFD0']['Orientation'] ?? 1;
$orientations = [
1 => '正常',
3 => '旋转180°',
6 => '顺时针旋转90°',
8 => '逆时针旋转90°'
];
return $orientations[$orientation] ?? '未知';
}
// 辅助方法:转换GPS坐标
private function convertDMSToDecimal($dmsArray)
{
if (!is_array($dmsArray) || count($dmsArray) != 3) {
return null;
}
$degrees = $this->convertFraction($dmsArray[0]);
$minutes = $this->convertFraction($dmsArray[1]);
$seconds = $this->convertFraction($dmsArray[2]);
return $degrees + ($minutes / 60) + ($seconds / 3600);
}
// 辅助方法:转换分数格式
private function convertFraction($fraction)
{
if (is_numeric($fraction)) {
return (float) $fraction;
}
$parts = explode('/', $fraction);
if (count($parts) == 2 && $parts[1] != 0) {
return (float) ($parts[0] / $parts[1]);
}
return 0;
}
// 辅助方法:获取GPS值
private function getGPSValue($coordKey, $refKey)
{
if (!isset($this->exifData['GPS'][$coordKey])) {
return null;
}
$coord = $this->exifData['GPS'][$coordKey];
$ref = $this->exifData['GPS'][$refKey] ?? '';
return ['coordinate' => $coord, 'ref' => $ref];
}
// 获取所有EXIF数据
public function getAllData()
{
return [
'camera' => $this->getCameraInfo(),
'settings' => $this->getCameraSettings(),
'gps' => $this->getGPSInfo(),
'datetime' => $this->getDateTimeInfo(),
'image' => $this->getImageInfo()
];
}
}
// 使用示例
try {
$reader = new ExifReader('photo.jpg');
$data = $reader->getAllData();
echo "<pre>";
print_r($data);
echo "</pre>";
} catch (Exception $e) {
echo "错误: " . $e->getMessage();
}
?>
使用第三方库(推荐)
使用 composer 安装
composer require pel/pel composer require php-exif/php-exif
使用 php-exif 库
<?php
require_once 'vendor/autoload.php';
use PHPExif\Exif;
use PHPExif\Reader\Reader;
// 创建读取器
$reader = Reader::factory(Reader::TYPE_EXIFTOOL); // 或 TYPE_NATIVE
// 读取EXIF数据
$exif = $reader->read('photo.jpg');
if ($exif) {
echo "标题: " . $exif->getTitle() . "\n";
echo "描述: " . $exif->getDescription() . "\n";
echo "拍摄时间: " . $exif->getCreationDate()->format('Y-m-d H:i:s') . "\n";
echo "相机型号: " . $exif->getCamera() . "\n";
echo "焦距: " . $exif->getFocalLength() . "mm\n";
echo "光圈: f/" . $exif->getAperture() . "\n";
echo "ISO: " . $exif->getIso() . "\n";
echo "快门速度: " . $exif->getExposure() . "\n";
echo "GPS纬度: " . $exif->getLatitude() . "\n";
echo "GPS经度: " . $exif->getLongitude() . "\n";
echo "方向: " . $exif->getOrientation() . "\n";
}
?>
使用 PEL 库(更专业)
<?php
require_once 'vendor/autoload.php';
use lsolesen\pel\PelJpeg;
use lsolesen\pel\PelTag;
// 读取JPEG文件
$file = new PelJpeg('photo.jpg');
$exif = $file->getExif();
if ($exif !== null) {
$tiff = $exif->getTiff();
if ($tiff !== null) {
$ifd0 = $tiff->getIfd();
// 获取相机信息
$make = $ifd0->getEntry(PelTag::MAKE);
$model = $ifd0->getEntry(PelTag::MODEL);
echo "制造商: " . ($make ? $make->getValue() : '未知') . "\n";
echo "型号: " . ($model ? $model->getValue() : '未知') . "\n";
// 获取拍摄设置
$exifIfd = $ifd0->getSubIfd(PelIfd::EXIF);
if ($exifIfd !== null) {
$iso = $exifIfd->getEntry(PelTag::ISO_SPEED_RATINGS);
$fNumber = $exifIfd->getEntry(PelTag::FNUMBER);
$focalLength = $exifIfd->getEntry(PelTag::FOCAL_LENGTH);
echo "ISO: " . ($iso ? $iso->getValue() : '未知') . "\n";
echo "光圈: f/" . ($fNumber ? $fNumber->getValue() : '未知') . "\n";
echo "焦距: " . ($focalLength ? $focalLength->getValue() : '未知') . "mm\n";
}
}
}
?>
读取其他文件类型的元数据
<?php
// 读取PDF元数据
function getPdfMetadata($filePath)
{
$content = file_get_contents($filePath);
$metadata = [];
// 提取基本PDF信息
preg_match('/\/Title\s*\(([^)]*)\)/', $content, $matches);
$metadata['title'] = $matches[1] ?? '未知';
preg_match('/\/Author\s*\(([^)]*)\)/', $content, $matches);
$metadata['author'] = $matches[1] ?? '未知';
preg_match('/\/Subject\s*\(([^)]*)\)/', $content, $matches);
$metadata['subject'] = $matches[1] ?? '未知';
preg_match('/\/Keywords\s*\(([^)]*)\)/', $content, $matches);
$metadata['keywords'] = $matches[1] ?? '未知';
// 获取页面数
preg_match('/\/Type\s*\/Pages[^>]*\/Count\s*(\d+)/', $content, $matches);
$metadata['pages'] = $matches[1] ?? '未知';
return $metadata;
}
// 读取Word文档元数据
function getDocxMetadata($filePath)
{
$zip = new ZipArchive();
if ($zip->open($filePath) === true) {
$content = $zip->getFromName('docProps/core.xml');
$zip->close();
if ($content) {
$xml = simplexml_load_string($content);
$namespaces = $xml->getNamespaces(true);
$metadata = [];
$metadata['title'] = (string)$xml->children('dc', true)->title;
$metadata['creator'] = (string)$xml->children('dc', true)->creator;
$metadata['description'] = (string)$xml->children('dc', true)->description;
$metadata['created'] = (string)$xml->children('dcterms', true)->created;
$metadata['modified'] = (string)$xml->children('dcterms', true)->modified;
return $metadata;
}
}
return null;
}
// 读取MP3元数据
function getMp3Metadata($filePath)
{
// 使用getID3库(composer require james-heinrich/getid3)
require_once 'vendor/autoload.php';
$getID3 = new getID3();
$file = $getID3->analyze($filePath);
if (isset($file['tags']['id3v2'])) {
$tags = $file['tags']['id3v2'];
return [
'title' => $tags['title'][0] ?? '未知',
'artist' => $tags['artist'][0] ?? '未知',
'album' => $tags['album'][0] ?? '未知',
'year' => $tags['year'][0] ?? '未知',
'genre' => $tags['genre'][0] ?? '未知',
'duration' => $file['playtime_seconds'] ?? 0,
'bitrate' => $file['audio']['bitrate'] ?? 0,
'sample_rate' => $file['audio']['sample_rate'] ?? 0
];
}
return null;
}
?>
实用工具函数
<?php
// 修正图片方向(根据EXIF方向信息)
function fixImageOrientation($sourcePath, $destinationPath = null)
{
if (!function_exists('exif_read_data')) {
return false;
}
$exif = @exif_read_data($sourcePath);
if ($exif === false) {
return false;
}
$orientation = $exif['IFD0']['Orientation'] ?? 1;
$image = imagecreatefromjpeg($sourcePath);
if ($image === false) {
return false;
}
switch ($orientation) {
case 2: // 水平翻转
imageflip($image, IMG_FLIP_HORIZONTAL);
break;
case 3: // 旋转180度
$image = imagerotate($image, 180, 0);
break;
case 4: // 垂直翻转
imageflip($image, IMG_FLIP_VERTICAL);
break;
case 5: // 水平翻转后旋转90度
imageflip($image, IMG_FLIP_HORIZONTAL);
$image = imagerotate($image, -90, 0);
break;
case 6: // 旋转90度
$image = imagerotate($image, -90, 0);
break;
case 7: // 垂直翻转后旋转90度
imageflip($image, IMG_FLIP_VERTICAL);
$image = imagerotate($image, -90, 0);
break;
case 8: // 旋转270度
$image = imagerotate($image, 90, 0);
break;
}
$destPath = $destinationPath ?? $sourcePath;
imagejpeg($image, $destPath, 100);
imagedestroy($image);
return true;
}
// 清除EXIF数据(保护隐私)
function stripExifData($sourcePath, $destinationPath)
{
$image = imagecreatefromjpeg($sourcePath);
if ($image === false) {
return false;
}
// 创建新图像,不包含EXIF数据
$newImage = imagecreatetruecolor(imagesx($image), imagesy($image));
imagecopy($newImage, $image, 0, 0, 0, 0, imagesx($image), imagesy($image));
imagejpeg($newImage, $destinationPath, 100);
imagedestroy($image);
imagedestroy($newImage);
return true;
}
// 批量处理文件夹中的图片
function batchProcessImages($directory, $callback)
{
$files = glob($directory . '/*.{jpg,jpeg,png,gif}', GLOB_BRACE);
$results = [];
foreach ($files as $file) {
try {
$result = $callback($file);
$results[$file] = $result;
} catch (Exception $e) {
$results[$file] = '错误: ' . $e->getMessage();
}
}
return $results;
}
?>
配置文件示例
<?php
// config/exif.php
return [
// EXIF读取设置
'exif' => [
'enabled' => true,
'read_all_tags' => false,
'thumbnail' => false,
'sections' => [
'FILE',
'COMPUTED',
'IFD0',
'THUMBNAIL',
'COMMENT',
'EXIF',
'GPS',
'INTEROP',
'MAKERNOTE'
]
],
// 支持的图片格式
'supported_formats' => [
'image/jpeg',
'image/tiff',
'image/png'
],
// GPS坐标格式
'gps_format' => 'decimal', // decimal, dms
// 缓存设置
'cache' => [
'enabled' => true,
'ttl' => 3600, // 1小时
'driver' => 'file' // file, redis, memcached
],
// 隐私设置
'privacy' => [
'strip_gps' => false,
'strip_copyright' => false,
'strip_all' => false
]
];
?>
注意事项
-
PHP配置:确保php.ini中启用了EXIF扩展
extension=exif
-
安全性:
- 上传图片时注意安全检查
- 用户隐私保护(GPS位置信息)
- 文件大小限制
-
性能优化:
- 对大文件使用流式处理
- 实现缓存机制
- 异步处理批量操作
-
错误处理:
- 文件不存在
- 格式不支持
- 扩展未启用
- 损坏的文件
这个完整的EXIF和元数据读取解决方案可以满足大多数PHP项目的需求,根据具体应用场景选择合适的实现方式。
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播