如何在上传图片时提取元数据并存入MySQL数据库
实现图片上传时的元数据获取与存储
1. 先更新数据库表结构
首先需要给images表添加存储元数据的字段,执行以下SQL语句:
ALTER TABLE images ADD COLUMN camera_model VARCHAR(255) NULL, ADD COLUMN gps_latitude DECIMAL(10,8) NULL, ADD COLUMN gps_longitude DECIMAL(11,8) NULL, ADD COLUMN shoot_period ENUM('day','night') NULL;
2. 改进EXIF元数据读取逻辑
替换原有的getExif函数,改成能批量读取所需元数据的逻辑,同时处理GPS坐标的格式转换:
// 转换EXIF GPS度分秒为十进制坐标 function convertGpsToDecimal($gpsData, $ref) { $degrees = $gpsData[0]; $minutes = $gpsData[1]; $seconds = $gpsData[2]; $decimal = $degrees + ($minutes / 60) + ($seconds / 3600); // 根据参考方向判断正负(南纬、西经为负) return ($ref == 'S' || $ref == 'W') ? -$decimal : $decimal; } // 获取图片的所有所需元数据 function getImageMetadata($tmpFilePath) { $metadata = [ 'camera_model' => '', 'gps_lat' => null, 'gps_lng' => null, 'shoot_period' => 'day' ]; $allowedTypes = ['jpg', 'jpeg', 'tif', 'tiff']; $fileExt = strtolower(pathinfo($tmpFilePath, PATHINFO_EXTENSION)); if (!in_array($fileExt, $allowedTypes)) { return $metadata; } $exif = exif_read_data($tmpFilePath, 0, true); if (!$exif) { return $metadata; } // 获取相机型号 if (isset($exif['IFD0']['Model'])) { $metadata['camera_model'] = $exif['IFD0']['Model']; } // 获取GPS坐标 if (isset($exif['GPS']) && isset($exif['GPS']['GPSLatitude'], $exif['GPS']['GPSLatitudeRef'], $exif['GPS']['GPSLongitude'], $exif['GPS']['GPSLongitudeRef'])) { $metadata['gps_lat'] = convertGpsToDecimal($exif['GPS']['GPSLatitude'], $exif['GPS']['GPSLatitudeRef']); $metadata['gps_lng'] = convertGpsToDecimal($exif['GPS']['GPSLongitude'], $exif['GPS']['GPSLongitudeRef']); } // 判断拍摄时段(白天/夜晚) if (isset($exif['EXIF']['DateTimeOriginal'])) { $shootTime = DateTime::createFromFormat('Y:m:d H:i:s', $exif['EXIF']['DateTimeOriginal']); if ($shootTime) { $hour = $shootTime->format('H'); $metadata['shoot_period'] = ($hour >= 6 && $hour <= 18) ? 'day' : 'night'; } } return $metadata; }
3. 修改上传逻辑,整合元数据存储
修改原有的上传循环部分,使用预处理语句避免SQL注入,同时插入元数据:
foreach($_FILES['files']['name'] as $key=>$val){ // 获取临时文件路径(要在移动文件前读取EXIF,因为移动后临时文件会被销毁) $tmpFilePath = $_FILES["files"]["tmp_name"][$key]; $fileName = basename($_FILES['files']['name'][$key]); $targetFilePath = $targetDir . $fileName; $fileType = pathinfo($targetFilePath, PATHINFO_EXTENSION); if(in_array($fileType, $allowTypes)){ // 先读取元数据 $metadata = getImageMetadata($tmpFilePath); // 读取图片宽度(复用原有逻辑) $width = getExif($tmpFilePath, "IFD0", "ImageWidth"); // 上传文件到服务器 if(move_uploaded_file($tmpFilePath, $targetFilePath)){ // 使用预处理语句插入数据,避免SQL注入 $stmt = $db->prepare("INSERT INTO images(file_name, uploaded_on, image_width, camera_model, gps_latitude, gps_longitude, shoot_period) VALUES (?, NOW(), ?, ?, ?, ?, ?)"); $stmt->bind_param("sdsdds", $fileName, $width, $metadata['camera_model'], $metadata['gps_lat'], $metadata['gps_lng'], $metadata['shoot_period']); $stmt->execute(); $stmt->close(); }else{ $errorUpload .= $_FILES['files']['name'][$key].' | '; } }else{ $errorUploadType .= $_FILES['files']['name'][$key].' | '; } }
关键注意事项
- 读取EXIF的时机:必须在
move_uploaded_file之前读取临时文件的EXIF,因为文件移动后临时文件会被系统销毁。 - SQL注入防护:原代码直接拼接SQL存在安全风险,改用预处理语句绑定参数是更安全的做法。
- 元数据兼容性:不是所有图片都包含完整的EXIF数据,必须做存在性判断,避免触发报错。
- GPS坐标转换:EXIF中的GPS是度分秒格式,转换为十进制后更方便后续的检索和地图展示。
内容的提问来源于stack exchange,提问作者Tom Meskin
相关产品推荐
相关产品推荐

