PHP ha integrato il supporto per la lettura dei metadati EXIF e IPTC, ma non riesco a trovare alcun modo per leggere XMP?Come posso leggere i dati XMP da un JPG con PHP?
risposta
I dati XMP sono letteralmente incorporati nel file immagine in modo da poterli estrarre con le funzioni stringa di PHP dal file immagine stesso.
Il seguito viene illustrato questa procedura (sto usando SimpleXML ma ogni altra API XML o anche semplice e intelligente analisi stringa può dare risultati uguali):
$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);
Solo due osservazioni:
- XMP fa un uso intensivo degli spazi dei nomi XML, quindi è necessario tenerlo d'occhio durante l'analisi dei dati XMP con alcuni strumenti XML.
- considerando la possibile dimensione dei file di immagine, forse non sarà possibile utilizzare
file_get_contents()
poiché questa funzione carica l'intera immagine in memoria. L'utilizzo difopen()
per aprire una risorsa di flusso di file e il controllo di blocchi di dati per le sequenze di chiavi<x:xmpmeta
e</x:xmpmeta>
ridurranno significativamente il footprint di memoria.
Sto solo rispondendo a questo dopo tanto tempo perché questo sembra essere il miglior risultato quando si cerca su Google come analizzare i dati XMP. Ho visto questo frammento quasi identico usato nel codice alcune volte ed è un terribile spreco di memoria. Ecco un esempio del metodo fopen() che Stefan cita dopo il suo esempio.
<?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;
}
vale la pena notare che questo si blocca quando l'immagine non contiene dati XMP, anche se sono sicuro che questo può essere facilmente risolto da chi sa come. –
Ho aggiunto un'altra condizione \ break al ciclo while che uccide il ciclo se nessun elemento XMP esiste nel file –
Ho refactored questa funzione per copiare prima il blocco e quindi eseguire il rilevamento/modifica sul buffer piuttosto che provare a farlo contro i pezzi. –
ho sviluppato l'estensione Xmp Php Tookit: si tratta di un'estensione PHP5 basato sul XMP toolkit Adobe, che forniscono le principali classi e il metodo per leggere metadatas/scrittura/parse XMP da JPEG, PSD, pdf, video, audio ... Questa estensione è sotto licenza gpl. Presto sarà disponibile una nuova versione, per php 5.3 (ora compatibile solo con php 5.2.x), e dovrebbe essere disponibile su windows e macosx (ora solo per sistemi freebsd e linux). http://xmpphptoolkit.sourceforge.net/
Ho provato il tuo toolkit, ma non sono riuscito a compilarlo :(lamentandomi della mancanza di printf. "Xmp_toolkit/common/XMP_LibUtils.hpp: 179: 62: errore:" printf "non è stato dichiarato in questo ambito" – haggi
Un modo semplice su linux è chiamare il programma exiv2, disponibile in un pacchetto omonimo su debian.
$ exiv2 -e X extract image.jpg
produrrà image.xmp contenente XMP incorporato che ora è il vostro da analizzare.
La soluzione di Bryan era la migliore finora, ma aveva alcuni problemi quindi l'ho modificata per semplificarla e rimuovere alcune funzionalità.
C'erano tre problemi che ho trovato con la sua soluzione:
A) Se il pezzo estratto cade proprio tra una delle stringhe che stiamo cercando, ma non la troveranno. Piccole dimensioni del blocco hanno più probabilità di causare questo problema.
B) Se il blocco contiene sia l'inizio che la fine, non lo troverà. Questo è facile da risolvere con un'istruzione extra if per ricontrollare il blocco in cui si trova l'avvio per vedere se viene trovata anche la fine.
C) La dichiarazione else aggiunta alla fine per interrompere il ciclo while se non rileva che i dati xmp hanno un effetto collaterale che se l'elemento start non viene trovato al primo passaggio, non controllerà più pezzi. Questo è probabilmente facile da risolvere, ma con il primo problema non ne vale la pena.
La mia soluzione di seguito non è così potente, ma è più robusta. Controllerà solo un chunk ed estrarrà i dati da quello. Funzionerà solo se l'inizio e la fine sono in quel blocco, quindi la dimensione del blocco deve essere abbastanza grande da garantire che acquisisca sempre tali dati. Dalla mia esperienza con i file esportati da Adobe Photoshop/Lightroom, i dati xmp iniziano tipicamente intorno ai 20kB e terminano intorno ai 45kB. La mia dimensione di chunk di 50k sembra funzionare bene per le mie immagini, sarebbe molto meno se si eliminano alcuni di quei dati all'esportazione, come il blocco CRS che ha molte impostazioni di sviluppo.
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;
}
Ho aggiornato la mia funzione con correzioni per i problemi logici che aveva :) –
Ah, grazie Bryan! Non ho mai notato che tu abbia risposto fino ad ora. Esaminerò il tuo codice revisionato e vedrò se funziona per me (non lo capisco ancora del tutto, non sono un programmatore ...) –
Oooh, ho capito ora .. Stai costruendo il buffer uno pezzo alla volta e controllando sempre il buffer. Questo impedisce tutto il problema che ho elencato. Inteligente! Grazie. –
Grazie a voi Sebastien B. per quella versione abbreviata :). Se vuoi evitare il problema, quando chunk_size è troppo piccolo per alcuni file, aggiungi la ricorsione.
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;
}
lo so ... questo è una specie di un vecchio filo, ma è stato utile a me quando ero alla ricerca di un modo per fare questo, così ho pensato che questo potrebbe essere utile a qualcun altro.
Ho preso questa soluzione di base e l'ho modificata in modo che gestisse il caso in cui il tag è diviso tra i blocchi. Ciò consente alle dimensioni del blocco di essere grandi o piccole come si desidera.
<?php
function getXmpData($filename, $chunk_size = 1024)
{
\t if (!is_int($chunkSize)) {
\t \t throw new RuntimeException('Expected integer value for argument #2 (chunkSize)');
\t }
\t if ($chunkSize < 12) {
\t \t throw new RuntimeException('Chunk size cannot be less than 12 argument #2 (chunkSize)');
\t }
\t if (($file_pointer = fopen($filename, 'rb')) === FALSE) {
\t \t throw new RuntimeException('Could not open file for reading');
\t }
\t $tag = '<x:xmpmeta';
\t $buffer = false;
\t // find open tag
\t while ($buffer === false && ($chunk = fread($file_pointer, $chunk_size)) !== false) {
\t \t if(strlen($chunk) <= 10) {
\t \t \t break;
\t \t }
\t \t if(($position = strpos($chunk, $tag)) === false) {
\t \t \t // if open tag not found, back up just in case the open tag is on the split.
\t \t \t fseek($file_pointer, -10, SEEK_CUR);
\t \t } else {
\t \t \t $buffer = substr($chunk, $position);
\t \t }
\t }
\t if($buffer === false) {
\t \t fclose($file_pointer);
\t \t return false;
\t }
\t $tag = '</x:xmpmeta>';
\t $offset = 0;
\t while (($position = strpos($buffer, $tag, $offset)) === false && ($chunk = fread($file_pointer, $chunk_size)) !== FALSE && !empty($chunk)) {
\t \t $offset = strlen($buffer) - 12; // subtract the tag size just in case it's split between chunks.
\t \t $buffer .= $chunk;
\t }
\t fclose($file_pointer);
\t if($position === false) {
\t \t // this would mean the open tag was found, but the close tag was not. Maybe file corruption?
\t \t throw new RuntimeException('No close tag found. Possibly corrupted file.');
\t } else {
\t \t $buffer = substr($buffer, 0, $position + 12);
\t }
\t return $buffer;
}
?>
Se hai ExifTool a disposizione (uno strumento molto utile) e possibile eseguire comandi esterni, è possibile utilizzare è l'opzione per estrarre i dati XMP (-xmp:all
) e l'uscita in formato JSON (-json
), che puoi convertire facilmente in un oggetto PHP:
$command = 'exiftool -g -json -struct -xmp:all "'.$image_path.'"';
exec($command, $output, $return_var);
$metadata = implode('', $output);
$metadata = json_decode($metadata);
- 1. Posso leggere un file .TXT con PHP?
- 2. Come posso leggere i metadati PNG da PHP?
- 3. Come leggere i dati da NSDocumentDirectory
- 4. Leggere i dati CSV da un file
- 5. Come leggere i dati da Hbase?
- 6. C'è un modo per leggere/scrivere metadati XMP usando imagemagick?
- 7. Come leggere i dati di configurazione da un servlet
- 8. Come leggere i dati da un file in Lua
- 9. Come leggere i dati logici da un file in R
- 10. leggere i dati da file JSON
- 11. I dati casuali aggiunti a un JPG renderanno inutilizzabile?
- 12. Posso leggere correttamente i dati binari tramite XDomainRequest?
- 13. Come posso leggere i campioni da una lista AudioBuffer?
- 14. Come posso emulare una porta COM, scrivere dati e leggere i dati da essa?
- 15. Come leggere i bit da un file?
- 16. Come posso leggere i dati da un file di testo usando VB6?
- 17. Come espandere ms-explorer per gestire automaticamente "file collegati"/file sidecar/xmp appartenenti a jpg?
- 18. Come leggere un'immagine con PHP?
- 19. Come leggere i dati da file Excel utilizzando C#
- 20. Come posso scrivere i dati in un excel utilizzando PHP
- 21. Come leggere i dati della porta seriale da JavaScript
- 22. come posso leggere i dati binari in C++?
- 23. Come leggere i dati da Firebase ONCE usando java/android?
- 24. Come leggere e stampare i dati da mysql in C#
- 25. Come leggere i dati dal database SQLite?
- 26. Leggere i dati da un file di testo utilizzando Java
- 27. Come convertire una stringa SVG in un jpg con Inkscape
- 28. Come cancellare solo i dati delle immagini in un file jpg con dotnet?
- 29. Come leggere i dati hdf in Octave
- 30. Scrivi dati binari con Haskell da leggere con C?
Questo spiegherebbe perché non ci sono funzioni specifiche XMP in PHP. – Liam