首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

Java 讀取圖片 EXIF 信息完整實戰指南

     編輯:Java 相關知識

什麼是 EXIF?

EXIF (Exchangeable Image File Format) 是數碼相機照片專用的元數據標準,記錄拍攝參數(光圈、快門、ISO、焦距)、設備信息(品牌、型號)、時間、GPS 坐標、縮略圖等。主流支持格式:JPEG、TIFF、HEIC、RAW 等。

主流 Java 庫對比

庫Maven 坐標特點推薦指數
metadata-extractorcom.drewnoakes:metadata-extractor:2.18.0功能全、無依賴、活躍維護、支持最多格式⭐⭐⭐⭐⭐
Apache Sanselanorg.apache.commons:commons-imagingApache 項目、純 Java、功能較全⭐⭐⭐⭐
TwelveMonkeys ImageIOcom.twelvemonkeys.imageio:imageio-jpegImageIO 插件、支持更多格式讀寫⭐⭐⭐
JDK ImageIO內置無需依賴、功能極少、不推薦生產用⭐

metadata-extractor 核心用法

1. Maven 依賴

<dependency>
    <groupId>com.drewnoakes</groupId>
    <artifactId>metadata-extractor</artifactId>
    <version>2.18.0</version>
</dependency>

2. 讀取所有標籤

import com.drew.imaging.ImageMetadataReader;
import com.drew.metadata.*;
import java.io.File;
import java.io.IOException;

public class ExifReader {
    public static void main(String[] args) throws IOException, ImageProcessingException {
        File jpegFile = new File("photo.jpg");
        Metadata metadata = ImageMetadataReader.readMetadata(jpegFile);

        for (Directory directory : metadata.getDirectories()) {
            for (Tag tag : directory.getTags()) {
                System.out.printf("[%s] %s - %s%n",
                    directory.getName(), tag.getTagName(), tag.getDescription());
            }
            // 錯誤信息
            for (String error : directory.getErrors()) {
                System.err.printf("ERROR: %s - %s%n", directory.getName(), error);
            }
        }
    }
}

3. 精確提取常用字段

public static ExifInfo extractExif(File file) throws IOException, ImageProcessingException {
    Metadata metadata = ImageMetadataReader.readMetadata(file);
    ExifInfo info = new ExifInfo();

    // Exif 子目錄
    ExifSubIFDDirectory exif = metadata.getFirstDirectoryOfType(ExifSubIFDDirectory.class);
    if (exif != null) {
        info.setCameraMake(exif.getString(ExifDirectoryBase.TAG_MAKE));
        info.setCameraModel(exif.getString(ExifDirectoryBase.TAG_MODEL));
        info.setDateTime(exif.getDate(ExifDirectoryBase.TAG_DATETIME_ORIGINAL));
        info.setExposureTime(exif.getString(ExifDirectoryBase.TAG_EXPOSURE_TIME));
        info.setFNumber(exif.getString(ExifDirectoryBase.TAG_FNUMBER));
        info.setIso(exif.getInteger(ExifDirectoryBase.TAG_ISO_EQUIVALENT));
        info.setFocalLength(exif.getString(ExifDirectoryBase.TAG_FOCAL_LENGTH));
        info.setFlash(exif.getInteger(ExifDirectoryBase.TAG_FLASH) != 0);
    }

    // GPS 目錄
    GpsDirectory gps = metadata.getFirstDirectoryOfType(GpsDirectory.class);
    if (gps != null) {
        info.setLatitude(gps.getGeoLocation().getLatitude());
        info.setLongitude(gps.getGeoLocation().getLongitude());
    }

    // 縮略圖
    JpegDirectory jpeg = metadata.getFirstDirectoryOfType(JpegDirectory.class);
    if (jpeg != null && jpeg.hasThumbnail()) {
        info.setThumbnail(jpeg.getThumbnail());
    }

    return info;
}

4. 根據 EXIF 方向自動旋轉圖片

import java.awt.image.BufferedImage;
import java.io.File;
import javax.imageio.ImageIO;
import com.drew.metadata.exif.ExifDirectoryBase;

public static BufferedImage autoRotate(File imageFile) throws IOException {
    BufferedImage image = ImageIO.read(imageFile);
    Metadata metadata = ImageMetadataReader.readMetadata(imageFile);
    ExifDirectoryBase exif = metadata.getFirstDirectoryOfType(ExifDirectoryBase.class);
    
    if (exif == null) return image;
    
    int orientation = exif.getInt(ExifDirectoryBase.TAG_ORIENTATION);
    return switch (orientation) {
        case 1 -> image; // 正常
        case 2 -> flipHorizontal(image);
        case 3 -> rotate180(image);
        case 4 -> flipVertical(image);
        case 5 -> rotate90(flipHorizontal(image));
        case 6 -> rotate90(image);      // 順時針 90°
        case 7 -> rotate90(flipVertical(image));
        case 8 -> rotate270(image);     // 逆時針 90°
        default -> image;
    };
}

private static BufferedImage rotate90(BufferedImage img) {
    BufferedImage rotated = new BufferedImage(img.getHeight(), img.getWidth(), img.getType());
    Graphics2D g2d = rotated.createGraphics();
    g2d.rotate(Math.PI / 2, img.getHeight() / 2.0, img.getWidth() / 2.0);
    g2d.drawImage(img, 0, 0, null);
    g2d.dispose();
    return rotated;
}
// flipHorizontal, flipVertical, rotate180, rotate270 類似實現...

常見問題與解決方案

問題原因解決
讀取不到 EXIF圖片無 EXIF / 被截圖/編輯工具剝離檢查原圖、用 Windows 屬性查看確認
中文標籤亂碼字符編碼問題 (Windows XP 標題/作者等)metadata-extractor 2.16+ 已修復,升級版本
大文件 OOMImageIO.read 載入完整像素僅讀取元數據不解碼像素,用 ImageMetadataReader
HEIC/RAW 不支持庫版本過舊升級 metadata-extractor 2.18+,或用 TwelveMonkeys
Android 環境無標準文件路徑用 ContentResolver.openInputStream(uri) 獲取流

性能優化建議

  • 流式讀取:ImageMetadataReader.readMetadata(InputStream) 避免臨時文件
  • 僅讀取所需目錄:傳遞 new DirectoryFilter() 過濾
  • 緩存結果:同一文件多次讀取緩存 ExifInfo 對象
  • 異步處理:大批量導入時用線程池/CompletableFuture

總結

使用 metadata-extractor 是 Java 讀取 EXIF 的最佳選擇:API 清晰、格式支持全、無傳遞依賴、活躍維護。關鍵點:正確處理方向標籤自動旋轉、GPS 坐標解析、縮略圖提取、大文件內存控制。生產環境建議封裝為統一的 ExifService,統一異常處理、日誌、指標監控。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved