当前位置: 首页 > news >正文

java批量修改照片文件名为拍摄时间(Exif里获取)

手机里的照片,一年整理一次。拍摄时间在Exif中获取,若获取失败则使用创建时间。

package auto; import com.drew.imaging.ImageMetadataReader; import com.drew.imaging.ImageProcessingException; import com.drew.metadata.Metadata; import com.drew.metadata.exif.ExifSubIFDDirectory; import java.io.File; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.nio.file.attribute.BasicFileAttributes; import java.nio.file.attribute.FileTime; import java.text.SimpleDateFormat; import java.util.Date; import java.util.TimeZone; /** * @author rll * @description: 批量修改照片(jpg)时间 * @date 2026/8/7 */ public class PicRename { public static void main(String[] args) { //设置为GMT+8,小时时间对不上,特意改为GMT TimeZone.setDefault(TimeZone.getTimeZone("GMT")); //TODO 修改为你要处理的文件夹路径 String folderPath = "F:\\test"; File folder = new File(folderPath); if (!folder.exists() || !folder.isDirectory()) { System.out.println("文件夹不存在或不是一个目录: " + folderPath); return; } File[] files = folder.listFiles((dir, name) -> name.toLowerCase().endsWith(".jpg")); if (files == null || files.length == 0) { System.out.println("文件夹内没有 .jpg 文件"); return; } SimpleDateFormat sdf = new SimpleDateFormat("yyyy_MM_dd HH_mm_ss"); // EXIF 时间通常为本地时间,无需时区转换 sdf.setTimeZone(TimeZone.getDefault()); for (File file : files) { try { Date shootingTime = getShootingTime(file); String newName = sdf.format(shootingTime) + ".jpg"; File newFile = new File(folder, newName); // 处理重名:若文件已存在(且不是同一文件),添加序号 if (newFile.exists() && !file.getCanonicalPath().equals(newFile.getCanonicalPath())) { newFile = makeUniqueFile(folder, sdf.format(shootingTime), ".jpg"); } if (file.renameTo(newFile)) { System.out.println("重命名成功: " + file.getName() + " -> " + newFile.getName()); } else { System.out.println("重命名失败: " + file.getName()); } } catch (Exception e) { System.err.println("处理文件失败: " + file.getName() + ", 原因: " + e.getMessage()); } } } /** * 优先从 EXIF 获取拍摄时间,若获取失败则使用文件修改时间 */ private static Date getShootingTime(File file) throws IOException { try { Metadata metadata = ImageMetadataReader.readMetadata(file); ExifSubIFDDirectory exifDir = metadata.getFirstDirectoryOfType(ExifSubIFDDirectory.class); if (exifDir != null) { Date date = exifDir.getDateOriginal(); System.out.println("拍摄时间:"+exifDir); if (date != null) { return date; } } } catch (ImageProcessingException e) { // 无法解析 EXIF,使用文件时间 } // Fallback: 使用文件最后修改时间 Path path = Paths.get(file.getAbsolutePath()); BasicFileAttributes attrs = Files.readAttributes(path, BasicFileAttributes.class); FileTime fileTime = attrs.lastModifiedTime(); return new Date(fileTime.toMillis()); } /** * 生成不重名的文件,命名格式为 baseName_序号.jpg */ private static File makeUniqueFile(File directory, String baseName, String extension) { int count = 1; File candidate; do { candidate = new File(directory, baseName + "_" + count + extension); count++; } while (candidate.exists()); return candidate; } }
http://www.jsqmd.com/news/1358850/

相关文章:

  • 抖音下载神器:一键保存视频、直播回放与批量下载完整指南
  • 冲突管理:强 / 弱关系下情绪冲突与事实冲突完整体系
  • 零依赖Agent记忆存储方案:基于SQLite的Remembrane实战指南
  • UE5 GameFeature插件化架构:告别Pawn代码“屎山”,实现模块化开发
  • 职级・规模・考核:构建组织管理的闭环效率体系
  • WarcraftHelper终极方案:完美解决魔兽争霸III在现代系统上的兼容性问题
  • 大麦网抢票脚本终极指南:三步实现自动化抢票
  • 奇摩带你玩转:WorkBuddy驱动机器学习项目的自动化实践 - 奇摩-workbuddy
  • 让 AI 写你的视觉小说:renpy-mcp,让 Cursor 原生开发 Ren‘Py 的 MCP 服务器
  • Vibe Coding:应对模糊需求的直觉驱动编程模式解析
  • 江西科技学院博士招聘,应聘前要准备好这些材料 - 资讯报道
  • 系统性黑哨分析框架:以2014世界杯阿根廷VS瑞士为例的技术复盘
  • 情绪冲突‑情绪管理完整概念体系
  • 构建自动化音频处理流水线:从文件整理到音质增强的工程实践
  • 开源贡献者证书:从IvorySQL社区实践看如何参与开源并构建个人技术品牌
  • 【ubuntu安装教程图解】最新VMware虚拟机安装ubuntu保姆级图文详解(附ISO镜像下载)
  • Nginx与TCP协议深度优化实践指南
  • 终极RPG Maker MV/MZ资源解密工具:免费快速解锁游戏资源
  • 3分钟搞定硬字幕转SRT:本地视频字幕提取神器完全指南
  • AI 文本配音工具记录:有声素材制作工具能力边界整理
  • 2026安徽高考志愿滑档了,还有学校可以上吗? - 小张zc
  • 2026年高性价比350型长螺旋桩机动力头厂家推荐及选型指南 - 全域品牌推荐
  • ArrayList 源码深度剖析(第 1 篇)
  • 观察者模式:原理、实现与应用场景详解
  • 社区技能目录构建实战:从数据模型到自动化工作流
  • 数据库并发安全:剖析竞态条件漏洞与事务锁实战防御
  • Unity集成WebP插件全攻略:优化包体与加载性能
  • 2026佛山系统门窗品牌**_八大居住场景匹配慧宁门窗等5家品牌差异化方案 - 资讯报道
  • 2026年8月鱼台县茶香板厂家推荐,全屋定制茶香板,茶香家具板,茶香生态板厂家优选指南! - 品牌商讯
  • 抖音下载器完整指南:3分钟学会无水印视频批量保存