PHP内置支持读取EXIF和IPTC元数据,但我找不到任何方法来读取XMP?
答案 0 :(得分:21)
XMP数据实际上嵌入到图像文件中,因此可以使用PHP的字符串函数从图像文件中提取它。
以下演示了这个过程(我正在使用SimpleXML,但是每个其他XML API甚至简单而聪明的字符串解析都可以给你相同的结果):
$content = file_get_contents($image);
$xmp_data_start = strpos($content, '<x:xmpmeta');
$xmp_data_end = strpos($content, '</x:xmpmeta>');
$xmp_length = $xmp_data_end - $xmp_data_start;
$xmp_data = substr($content, $xmp_data_start, $xmp_length + 12);
$xmp = simplexml_load_string($xmp_data);
只有两个评论:
file_get_contents()
,因为此函数会将整个图像加载到内存中。使用fopen()
打开文件流资源并检查密钥序列<x:xmpmeta
和</x:xmpmeta>
的数据块将显着减少内存占用。答案 1 :(得分:12)
我只是在这么多时间之后回复这个问题,因为这似乎是搜索Google如何解析XMP数据时的最佳结果。我已经在代码中看到了这个几乎相同的片段几次,这是对内存的可怕浪费。以下是Stefan在他的例子后提到的fopen()方法的一个例子。
<?php
function getXmpData($filename, $chunkSize)
{
if (!is_int($chunkSize)) {
throw new RuntimeException('Expected integer value for argument #2 (chunkSize)');
}
if ($chunkSize < 12) {
throw new RuntimeException('Chunk size cannot be less than 12 argument #2 (chunkSize)');
}
if (($file_pointer = fopen($filename, 'r')) === FALSE) {
throw new RuntimeException('Could not open file for reading');
}
$startTag = '<x:xmpmeta';
$endTag = '</x:xmpmeta>';
$buffer = NULL;
$hasXmp = FALSE;
while (($chunk = fread($file_pointer, $chunkSize)) !== FALSE) {
if ($chunk === "") {
break;
}
$buffer .= $chunk;
$startPosition = strpos($buffer, $startTag);
$endPosition = strpos($buffer, $endTag);
if ($startPosition !== FALSE && $endPosition !== FALSE) {
$buffer = substr($buffer, $startPosition, $endPosition - $startPosition + 12);
$hasXmp = TRUE;
break;
} elseif ($startPosition !== FALSE) {
$buffer = substr($buffer, $startPosition);
$hasXmp = TRUE;
} elseif (strlen($buffer) > (strlen($startTag) * 2)) {
$buffer = substr($buffer, strlen($startTag));
}
}
fclose($file_pointer);
return ($hasXmp) ? $buffer : NULL;
}
答案 2 :(得分:4)
linux上的一个简单方法是调用exiv2程序,该程序在debian上的同名包中提供。
$ exiv2 -e X extract image.jpg
将生成包含嵌入式XMP的image.xmp,现在是您的解析。
答案 3 :(得分:3)
我知道......这是一个古老的线索,但是当我在寻找一种方法时,这对我很有帮助,所以我认为这可能对其他人有所帮助。
我采用了这个基本的解决方案并对其进行了修改,以便处理标签在块之间分割的情况。这允许块大小可以根据需要大小。
<?php
function getXmpData($filename, $chunk_size = 1024)
{
if (!is_int($chunkSize)) {
throw new RuntimeException('Expected integer value for argument #2 (chunkSize)');
}
if ($chunkSize < 12) {
throw new RuntimeException('Chunk size cannot be less than 12 argument #2 (chunkSize)');
}
if (($file_pointer = fopen($filename, 'rb')) === FALSE) {
throw new RuntimeException('Could not open file for reading');
}
$tag = '<x:xmpmeta';
$buffer = false;
// find open tag
while ($buffer === false && ($chunk = fread($file_pointer, $chunk_size)) !== false) {
if(strlen($chunk) <= 10) {
break;
}
if(($position = strpos($chunk, $tag)) === false) {
// if open tag not found, back up just in case the open tag is on the split.
fseek($file_pointer, -10, SEEK_CUR);
} else {
$buffer = substr($chunk, $position);
}
}
if($buffer === false) {
fclose($file_pointer);
return false;
}
$tag = '</x:xmpmeta>';
$offset = 0;
while (($position = strpos($buffer, $tag, $offset)) === false && ($chunk = fread($file_pointer, $chunk_size)) !== FALSE && !empty($chunk)) {
$offset = strlen($buffer) - 12; // subtract the tag size just in case it's split between chunks.
$buffer .= $chunk;
}
fclose($file_pointer);
if($position === false) {
// this would mean the open tag was found, but the close tag was not. Maybe file corruption?
throw new RuntimeException('No close tag found. Possibly corrupted file.');
} else {
$buffer = substr($buffer, 0, $position + 12);
}
return $buffer;
}
?>
&#13;
答案 4 :(得分:1)
我开发了Xmp Php Tookit扩展:它是基于adobe xmp工具包的php5扩展,它提供了从jpeg,psd,pdf,video,audio读取/写入/解析xmp元数据的主要类和方法。 ..此扩展程序是在gpl许可下。对于php 5.3(现在只与php 5.2.x兼容),很快就会推出新版本,并且应该可以在windows和macosx上使用(现在只适用于freebsd和linux系统)。 http://xmpphptoolkit.sourceforge.net/
答案 5 :(得分:1)
Bryan的解决方案是迄今为止最好的解决方案,但它有一些问题,因此我对其进行了修改以简化它,并删除了一些功能。
我在他的解决方案中发现了三个问题:
A)如果提取的块落在我们正在搜索的其中一个字符串之间,则无法找到它。小块大小更有可能导致此问题。
B)如果块包含开始和结束,它将找不到它。这是一个很容易修复的额外if语句来重新检查找到开头的块,看看是否也找到了结尾。
C)如果没有找到xmp数据,则添加到结尾以断开while循环的else语句有副作用,如果在第一次传递时找不到start元素,它将不再检查块。这很容易解决,但第一个问题是不值得的。
我的解决方案不是那么强大,但它更强大。它只会检查一个块,并从中提取数据。它仅在开始和结束位于该块中时才起作用,因此块大小需要足够大以确保它始终捕获该数据。根据我使用Adobe Photoshop / Lightroom导出文件的经验,xmp数据通常从大约20kB开始,到45kB左右结束。我的大小为50k似乎对我的图像效果很好,如果你在导出时删除一些数据会少得多,例如具有大量开发设置的CRS块。
function getXmpData($filename)
{
$chunk_size = 50000;
$buffer = NULL;
if (($file_pointer = fopen($filename, 'r')) === FALSE) {
throw new RuntimeException('Could not open file for reading');
}
$chunk = fread($file_pointer, $chunk_size);
if (($posStart = strpos($chunk, '<x:xmpmeta')) !== FALSE) {
$buffer = substr($chunk, $posStart);
$posEnd = strpos($buffer, '</x:xmpmeta>');
$buffer = substr($buffer, 0, $posEnd + 12);
}
fclose($file_pointer);
return $buffer;
}
答案 6 :(得分:1)
谢谢Sebastien B.的缩短版:)。如果你想避免这个问题,当chunk_size对于某些文件而言太小时,只需添加递归。
function getXmpData($filename, $chunk_size = 50000){
$buffer = NULL;
if (($file_pointer = fopen($filename, 'r')) === FALSE) {
throw new RuntimeException('Could not open file for reading');
}
$chunk = fread($file_pointer, $chunk_size);
if (($posStart = strpos($chunk, '<x:xmpmeta')) !== FALSE) {
$buffer = substr($chunk, $posStart);
$posEnd = strpos($buffer, '</x:xmpmeta>');
$buffer = substr($buffer, 0, $posEnd + 12);
}
fclose($file_pointer);
// recursion here
if(!strpos($buffer, '</x:xmpmeta>')){
$buffer = getXmpData($filename, $chunk_size*2);
}
return $buffer;
}
答案 7 :(得分:1)
如果你有ExifTool(一个非常有用的工具)并且可以运行外部命令,你可以使用它来提取XMP数据(-xmp:all
)并以JSON格式(-json
)输出,然后,您可以轻松地将其转换为PHP对象:
$command = 'exiftool -g -json -struct -xmp:all "'.$image_path.'"';
exec($command, $output, $return_var);
$metadata = implode('', $output);
$metadata = json_decode($metadata);