如何仅读取必要字节,从流式媒体中提取日期时间元数据?
从流式媒体中读取日期元数据(无需加载完整文件)
针对你的场景,metadata-extractor 本身支持从 InputStream 读取元数据,且多数图片/视频的日期元数据位于文件头部,无需加载完整媒体。关键是要处理流的复用,确保读取元数据后,后续生成缩略图的流程仍能正常读取完整流。
实现方案
- 将管道输入流包装为可标记/重置的缓冲流,让元数据读取完成后能回退到流的起始位置。
- 使用
metadata-extractor从缓冲流中读取元数据,它会自动在获取到足够信息后停止读取。 - 重置缓冲流,供后续缩略图生成流程使用。
修改后的代码示例
import com.drew.imaging.ImageMetadataReader; import com.drew.metadata.Metadata; import com.drew.metadata.exif.ExifSubIFDDirectory; import com.drew.metadata.mp4.Mp4Directory; import java.io.BufferedInputStream; import java.io.IOException; import java.util.Date; import java.util.logging.Level; import java.util.logging.Logger; public class MediaProcessor { private static final Logger LOGGER = Logger.getLogger(MediaProcessor.class.getName()); // 设置足够大的标记大小,适配多数媒体的头部元数据(1MB足够覆盖绝大多数场景) private static final int MARK_LIMIT = 1024 * 1024; public static void createThumbnailImage(Media media, TmpMedia tmpMedia) throws IOException { PipedInputStream mediaIn = new PipedInputStream(); PipedOutputStream mediaOut = new PipedOutputStream(mediaIn); // 启动线程读取媒体到管道输出流 new Thread(() -> { try { App.storage.getMedia(media, mediaOut); } catch (IOException e) { LOGGER.log(Level.WARNING, "Error getting Media for thumbnail", e); } finally { try { mediaOut.close(); } catch (IOException e) { LOGGER.log(Level.SEVERE, "Failed to close media output stream", e); } } }).start(); // 包装为可标记的缓冲流 BufferedInputStream bufferedMediaIn = new BufferedInputStream(mediaIn, MARK_LIMIT); bufferedMediaIn.mark(MARK_LIMIT); // 读取日期元数据 Date mediaDateTime = null; try { Metadata metadata = ImageMetadataReader.readMetadata(bufferedMediaIn); // 读取图片EXIF原始日期 ExifSubIFDDirectory exifDir = metadata.getFirstDirectoryOfType(ExifSubIFDDirectory.class); if (exifDir != null) { mediaDateTime = exifDir.getDate(ExifSubIFDDirectory.TAG_DATETIME_ORIGINAL); } // 读取MP4视频创建时间(按需添加其他格式支持) if (mediaDateTime == null) { Mp4Directory mp4Dir = metadata.getFirstDirectoryOfType(Mp4Directory.class); if (mp4Dir != null) { mediaDateTime = mp4Dir.getDate(Mp4Directory.TAG_CREATION_TIME); } } } catch (Exception e) { LOGGER.log(Level.WARNING, "Failed to read media metadata", e); } finally { // 重置流到起始位置,供后续缩略图生成使用 try { bufferedMediaIn.reset(); } catch (IOException e) { LOGGER.log(Level.SEVERE, "Failed to reset media stream", e); throw e; } } // 业务逻辑处理日期(示例) if (mediaDateTime != null) { LOGGER.info("Media date time: " + mediaDateTime); } // 后续缩略图生成逻辑,使用bufferedMediaIn代替原mediaIn // Rest of code... // Example: ImageIO.read(bufferedMediaIn); } }
关键注意事项
- 标记大小设置:
MARK_LIMIT需足够容纳媒体头部的元数据,1MB对绝大多数图片和视频都足够;如果遇到元数据在文件尾部的格式(如少数MP4文件),这种方式可能无法读取,此时需结合业务场景评估是否需要特殊处理。 - 流的关闭:必须确保管道输出流在读取完成后关闭,避免资源泄漏。
- 多格式扩展:
metadata-extractor支持几十种媒体格式,可根据业务中涉及的媒体类型,添加对应元数据目录的读取逻辑。
内容的提问来源于stack exchange,提问作者Hein Gertenbach
相关产品推荐
相关产品推荐

