PHP中利用EXIF函数集来显示单反照片的EXIF信息

本文介绍如何通过修改PHP配置并利用EXIF函数集显示图片的EXIF信息,包括详细步骤和示例代码。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

要成功做到能用EXIF函数显示单反EXIF信息需要一下几个不步骤:

1.修改php.ini的exif相关信息

2.继续修改php.ini信息,找到以下模块,将带exif的配置属性前的;号去掉,然后重启apache

3.验证php确实开启了exif模块成功,利用以下代码

<?php
echo phpinfo();
?>
如果看到显示下图,则表示成功开启了exif模块,这样我们就可以开始利用其函数集了

4.开始写相关类(或函数,是别人封装好的,我个人觉得这个合适),如下,大家可以自己自行修改其中代码以供自己使用

<?php
function GetImageInfoVal($ImageInfo,$val_arr) {
$InfoVal = "未知";
foreach($val_arr as $name=>$val) {
if ($name==$ImageInfo) {
$InfoVal = &$val;
break;
}
}
return $InfoVal;
}
function GetImageInfo($img) {
$imgtype = array("", "GIF", "JPG", "PNG", "SWF", "PSD", "BMP", "TIFF(intel byte order)", "TIFF(motorola byte order)", "JPC", "JP2", "JPX", "JB2", "SWC", "IFF", "WBMP", "XBM");
$Orientation = array("", "top left side", "top right side", "bottom right side", "bottom left side", "left side top", "right side top", "right side bottom", "left side bottom");
$ResolutionUnit = array("", "", "英寸", "厘米");
$YCbCrPositioning = array("", "the center of pixel array", "the datum point");
$ExposureProgram = array("未定义", "手动", "标准程序", "光圈先决", "快门先决", "景深先决", "运动模式", "肖像模式", "风景模式");
$MeteringMode_arr = array(
"0" => "未知",
"1" => "平均",
"2" => "中央重点平均测光",
"3" => "点测",
"4" => "分区",
"5" => "评估",
"6" => "局部",
"255" => "其他"
);
$Lightsource_arr = array(
"0" => "未知",
"1" => "日光",
"2" => "荧光灯",
"3" => "钨丝灯",
"10" => "闪光灯",
"17" => "标准灯光A",
"18" => "标准灯光B",
"19" => "标准灯光C",
"20" => "D55",
"21" => "D65",
"22" => "D75",
"255" => "其他"
);
$Flash_arr = array(
"0" => "flash did not fire",
"1" => "flash fired",
"5" => "flash fired but strobe return light not detected",
"7" => "flash fired and strobe return light detected",
);

$exif = exif_read_data ($img,"IFD0");
if ($exif===false) {
$new_img_info = array ("文件信息" => "没有图片EXIF信息");
}
else
{
$exif = exif_read_data ($img,0,true);
$new_img_info = array (
"文件信息" => "-----------------------------",
"文件名" => $exif[FILE][FileName],
"文件类型" => $imgtype[$exif[FILE][FileType]],
"文件格式" => $exif[FILE][MimeType],
"文件大小" => $exif[FILE][FileSize],
"时间戳" => date("Y-m-d H:i:s",$exif[FILE][FileDateTime]),
"图像信息" => "-----------------------------",
"图片说明" => $exif[IFD0][ImageDescription],
"制造商" => $exif[IFD0][Make],
"型号" => $exif[IFD0][Model],
"方向" => $Orientation[$exif[IFD0][Orientation]],
"水平分辨率" => $exif[IFD0][XResolution].$ResolutionUnit[$exif[IFD0][ResolutionUnit]],
"垂直分辨率" => $exif[IFD0][YResolution].$ResolutionUnit[$exif[IFD0][ResolutionUnit]],
"创建软件" => $exif[IFD0][Software],
"修改时间" => $exif[IFD0][DateTime],
"作者" => $exif[IFD0][Artist],
"YCbCr位置控制" => $YCbCrPositioning[$exif[IFD0][YCbCrPositioning]],
"版权" => $exif[IFD0][Copyright],
"摄影版权" => $exif[COMPUTED][Copyright.Photographer],
"编辑版权" => $exif[COMPUTED][Copyright.Editor],
"拍摄信息" => "-----------------------------",
"Exif版本" => $exif[EXIF][ExifVersion],
"FlashPix版本" => "Ver. ".number_format($exif[EXIF][FlashPixVersion]/100,2),
"拍摄时间" => $exif[EXIF][DateTimeOriginal],
"数字化时间" => $exif[EXIF][DateTimeDigitized],
"拍摄分辨率高" => $exif[COMPUTED][Height],
"拍摄分辨率宽" => $exif[COMPUTED][Width],
/*
The actual aperture value of lens when the image was taken.
Unit is APEX.
To convert this value to ordinary F-number(F-stop),
calculate this value's power of root 2 (=1.4142).
For example, if the ApertureValue is '5', F-number is pow(1.41425,5) = F5.6.
*/
"光圈" => $exif[EXIF][ApertureValue],
"快门速度" => $exif[EXIF][ShutterSpeedValue],
"快门光圈" => $exif[COMPUTED][ApertureFNumber],
"最大光圈值" => "F".$exif[EXIF][MaxApertureValue],
"曝光时间" => $exif[EXIF][ExposureTime],
"F-Number" => $exif[EXIF][FNumber],
"测光模式" => GetImageInfoVal($exif[EXIF][MeteringMode],$MeteringMode_arr),
"光源" => GetImageInfoVal($exif[EXIF][LightSource], $Lightsource_arr),
"闪光灯" => GetImageInfoVal($exif[EXIF][Flash], $Flash_arr),
"曝光模式" => ($exif[EXIF][ExposureMode]==1?"手动":"自动"),
"白平衡" => ($exif[EXIF][WhiteBalance]==1?"手动":"自动"),
"曝光程序" => $ExposureProgram[$exif[EXIF][ExposureProgram]],
/*
Brightness of taken subject, unit is APEX. To calculate Exposure(Ev) from BrigtnessValue(Bv), you must add SensitivityValue(Sv).
Ev=Bv+Sv Sv=log((ISOSpeedRating/3.125),2)
ISO100:Sv=5, ISO200:Sv=6, ISO400:Sv=7, ISO125:Sv=5.32.
*/
"曝光补偿" => $exif[EXIF][ExposureBiasValue]."EV",
"ISO感光度" => $exif[EXIF][ISOSpeedRatings],
"分量配置" => (bin2hex($exif[EXIF][ComponentsConfiguration])=="01020300"?"YCbCr":"RGB"),//'0x04,0x05,0x06,0x00'="RGB" '0x01,0x02,0x03,0x00'="YCbCr"
"图像压缩率" => $exif[EXIF][CompressedBitsPerPixel]."Bits/Pixel",
"对焦距离" => $exif[COMPUTED][FocusDistance]."m",
"焦距" => $exif[EXIF][FocalLength]."mm",
"等价35mm焦距" => $exif[EXIF][FocalLengthIn35mmFilm]."mm",
/*
Stores user comment. This tag allows to use two-byte character code or unicode. First 8 bytes describe the character code. 'JIS' is a Japanese character code (known as Kanji).
'0x41,0x53,0x43,0x49,0x49,0x00,0x00,0x00':ASCII
'0x4a,0x49,0x53,0x00,0x00,0x00,0x00,0x00':JIS
'0x55,0x4e,0x49,0x43,0x4f,0x44,0x45,0x00':Unicode
'0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00':Undefined
*/
"用户注释编码" => $exif[COMPUTED][UserCommentEncoding],
"用户注释" => $exif[COMPUTED][UserComment],
"色彩空间" => ($exif[EXIF][ColorSpace]==1?"sRGB":"Uncalibrated"),
"Exif图像宽度" => $exif[EXIF][ExifImageLength],
"Exif图像高度" => $exif[EXIF][ExifImageWidth],
"文件来源" => (bin2hex($exif[EXIF][FileSource])==0x03?"digital still camera":"unknown"),
"场景类型" => (bin2hex($exif[EXIF][SceneType])==0x01?"A directly photographed image":"unknown"),
"缩略图文件格式" => $exif[COMPUTED][Thumbnail.FileType],
"缩略图Mime格式" => $exif[COMPUTED][Thumbnail.MimeType]
);
}
return $new_img_info;
}
5.写测试代码

<?php
header("content-type:text/html;charset=utf-8");
$filename = "test.jpg";
require 'pic.class.php';
$new_img_info = GetImageInfo($filename);
//echo $new_img_info;
foreach ($new_img_info as $key=> $section) {
echo $key.":".$section."<hr>";
}
?>
6.结果如图所示

文件信息:-----------------------------
文件名:test.jpg
文件类型:JPG
文件格式:image/jpeg
文件大小:477883
时间戳:2014-03-07 14:53:57
图像信息:-----------------------------
图片说明:SONY DSC
制造商:SONY
型号:DSLR-A900
方向:top left side
水平分辨率:2400000/10000英寸
垂直分辨率:2400000/10000英寸
创建软件:Adobe Photoshop CS6 (Windows)
修改时间:2014:01:13 14:02:59
作者:
YCbCr位置控制:
版权:
摄影版权:
编辑版权:
拍摄信息:-----------------------------
Exif版本:0221
FlashPix版本:Ver. 0.00
拍摄时间:2014:01:10 12:06:40
数字化时间:2014:01:10 12:06:40
拍摄分辨率高:806
拍摄分辨率宽:1210
光圈:761471/100000
快门速度:7965784/1000000
快门光圈:f/14.0
最大光圈值:F434/100
曝光时间:1/250
F-Number:14/1
测光模式:评估
光源:未知
闪光灯:未知
曝光模式:自动
白平衡:自动
曝光程序:光圈先决
曝光补偿:-7/10EV
ISO感光度:100
分量配置:RGB
图像压缩率:Bits/Pixel
对焦距离:m
焦距:120/10mm
等价35mm焦距:12mm
用户注释编码:
用户注释:
色彩空间:Uncalibrated
Exif图像宽度:4032
Exif图像高度:6048
文件来源:digital still camera
场景类型:A directly photographed image
缩略图文件格式:
缩略图Mime格式:

至此,一个简单利用exif函数集来显示图片的exif信息的模块就出来了.

import os import sys import glob import logging import traceback import re from PIL import Image from PIL.ExifTags import TAGS from datetime import datetime # 配置日志 logging.basicConfig( level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s', handlers=[ logging.FileHandler("photo_rename.log", encoding='utf-8'), logging.StreamHandler(sys.stdout) ] ) def normalize_path(path): """规范化路径:处理各种输入格式""" # 移除首尾空格和引号 path = path.strip().strip('"').strip("'") # 替换正斜杠为反斜杠(Windows) if sys.platform == 'win32': path = path.replace('/', '\\') # 处理单反斜杠转义问题 if not path.startswith('r"') and not path.startswith("r'"): path = path.replace('\\', '\\\\') # 处理原始字符串标记 if path.startswith('r"') or path.startswith("r'"): path = path[2:].strip('"').strip("'") # 确保路径存在 if not os.path.exists(path): logging.error(f"路径不存在: {path}") return None return path def get_exif_datetime(image_path): """从图片中提取拍摄时间""" try: with Image.open(image_path) as img: exif_data = img._getexif() if exif_data: for tag_id, value in exif_data.items(): tag_name = TAGS.get(tag_id, tag_id) if tag_name == "DateTimeOriginal": try: return datetime.strptime(value, "%Y:%m:%d %H:%M:%S") except ValueError: logging.warning(f"无效的日期格式: {value} in {image_path}") return None else: logging.warning(f"无EXIF数据: {image_path}") except Exception as e: logging.error(f"读取EXIF失败: {image_path} - {str(e)}") logging.debug(traceback.format_exc()) # 使用文件创建时间作为备选 try: ctime = os.path.getctime(image_path) return datetime.fromtimestamp(ctime) except Exception as e: logging.error(f"获取文件时间失败: {image_path} - {str(e)}") return datetime.now() def find_image_files(folder_path): """查找文件夹中所有图片文件(增强兼容性)""" # 支持的扩展名(不区分大小写) extensions = ['.jpg', '.jpeg', '.png', '.heic', '.tiff', '.bmp', '.gif', '.nef'] # 使用glob递归搜索 image_files = [] for ext in extensions: # 不区分大小写的搜索 pattern = os.path.join(folder_path, '**', f'*{ext}') image_files.extend(glob.glob(pattern, recursive=True)) # 大写扩展名搜索 pattern_upper = os.path.join(folder_path, '**', f'*{ext.upper()}') image_files.extend(glob.glob(pattern_upper, recursive=True)) return list(set(image_files)) # 去重 def rename_photos_by_time(folder_path, prefix="photo"): """按拍摄时间重命名文件夹中的照片""" # 规范化路径 folder_path = normalize_path(folder_path) if not folder_path: return False logging.info(f"处理路径: {folder_path}") # 查找所有图片文件 photo_files = find_image_files(folder_path) if not photo_files: logging.warning(f"未找到图片文件: {folder_path}") return False logging.info(f"找到 {len(photo_files)} 个图片文件") # 获取文件时间并排序 photos_with_time = [] for file_path in photo_files: try: capture_time = get_exif_datetime(file_path) photos_with_time.append((file_path, capture_time)) except Exception as e: logging.error(f"处理文件时出错: {file_path} - {str(e)}") logging.debug(traceback.format_exc()) if not photos_with_time: logging.error("无有效文件可处理") return False # 按时间排序 photos_with_time.sort(key=lambda x: x[1]) print("\n预览重命名操作(前10个文件):") for i, (file_path, _) in enumerate(photos_with_time[:10]): new_name = f"{prefix}_{i + 1:03d}{os.path.splitext(file_path)[1]}" print(f"{os.path.basename(file_path)} -> {new_name}") total_files = len(photos_with_time) print(f"\n总共 {total_files} 个文件将被重命名") # 确认操作 confirm = input("确认执行重命名操作? (y/n): ").strip().lower() if confirm != 'y': print("操作已取消") # 显示预览 return False # 重命名文件 success_count = 0 for idx, (old_path, _) in enumerate(photos_with_time, start=1): try: # 构造新文件名 ext = os.path.splitext(old_path)[1].lower() new_name = f"{prefix}_{idx:03d}{ext}" new_path = os.path.join(os.path.dirname(old_path), new_name) # 避免覆盖现有文件 counter = 1 base_new_path = new_path while os.path.exists(new_path): new_name = f"{prefix}_{idx:03d}_{counter}{ext}" new_path = os.path.join(os.path.dirname(old_path), new_name) counter += 1 # 执行重命名 os.rename(old_path, new_path) logging.info(f"重命名成功: {os.path.basename(old_path)} -> {new_name}") success_count += 1 except Exception as e: logging.error(f"重命名失败: {old_path} -> {new_path} - {str(e)}") logging.debug(traceback.format_exc()) logging.info(f"处理完成: {success_count}/{len(photos_with_time)} 个文件重命名成功") return success_count > 0 def main(): """主函数,提供更友好的路径输入引导""" print("=== 照片批量重命名工具 ===") print("注意: 此操作将直接修改文件名,请确保已备份重要文件") print("\n路径输入指南:") print("1. 直接输入路径,如: C:\\Users\\ZHD\\Desktop") print("2. 或将文件夹拖拽到终端窗口自动获取路径") print("3. 或输入路径使用正斜杠: C:/Users/ZHD/Desktop") # 获取用户输入 folder_path = input("\n请输入照片文件夹路径: ").strip() prefix = input("请输入文件名前缀 (默认为 'photo'): ").strip() prefix = prefix if prefix else "photo" # 执行重命名 print("\n开始处理...") result = rename_photos_by_time(folder_path, prefix) # 显示结果 if result: print("\n操作成功完成! 详细信息请查看日志文件 'photo_rename.log'") else: print("\n操作失败,请检查日志文件 'photo_rename.log' 中的错误信息") if __name__ == "__main__": main() 优化程序
最新发布
07-15
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值