Modifying XMP data with C #
I am using C # in ASP.NET version 2. I am trying to open an image file, read (and change) the XMP header and close it again. I cannot update ASP, so the WIC is missing and I just cannot figure out how to do it.
Here's what I have so far:
Bitmap bmp = new Bitmap(Server.MapPath(imageFile));
MemoryStream ms = new MemoryStream();
StreamReader sr = new StreamReader(Server.MapPath(imageFile));
*[stuff with find and replace here]*
byte[] data = ToByteArray(sr.ReadToEnd());
ms = new MemoryStream(data);
originalImage = System.Drawing.Image.FromStream(ms);
Any suggestions?
a source to share
How about this kind?
byte[] data = File.ReadAllBytes(path);
... find & replace bit here ...
File.WriteAllBytes(path, data);
Also, I really recommend not using System.Bitmap in an asp.net process as it leaks memory and crashes / accidentally crashes from time to time (even MS admits this)
Here's a bit from MS on why System.Drawing.Bitmap is unstable:
http://msdn.microsoft.com/en-us/library/system.drawing.aspx
"Warning: Classes in the System.Drawing namespace are not supported for use in a Windows service or ASP.NET. Attempting to use these classes from one of these application types may cause unexpected problems such as reduced service efficiency and runtime exceptions."
a source to share
Part 1 of the XMP 2012 spec, page 10 specifically talks about how to edit a file in place without requiring an understanding of the surrounding format (although they suggest this as a last resort). The built-in XMP package looks like this:
<?xpacket begin="■" id="W5M0MpCehiHzreSzNTczkc9d"?>
... the serialized XMP as described above: ...
<x:xmpmeta xmlns:x="adobe:ns:meta/">
<rdf:RDF xmlns:rdf= ...>
...
</rdf:RDF>
</x:xmpmeta>
... XML whitespace as padding ...
<?xpacket end="w"?>
In this example, 'is a Unicode "zero-width non-gap character" (U + FEFF) used as a byte order marker.
(XMP Spec 2010, Part 3, page 12) also gives specific byte patterns (UTF-8, UTF16, big / small endian) to search when scanning bytes. This will complement Chris's answer about reading a file as a giant stream of bytes.
a source to share
You can use the following functions to read / write binary data:
public byte[] GetBinaryData(string path, int bufferSize)
{
MemoryStream ms = new MemoryStream();
using (FileStream fs = File.Open(path, FileMode.Open, FileAccess.Read))
{
int bytesRead;
byte[] buffer = new byte[bufferSize];
while((bytesRead = fs.Read(buffer,0,bufferSize))>0)
{
ms.Write(buffer,0,bytesRead);
}
}
return(ms.ToArray());
}
public void SaveBinaryData(string path, byte[] data, int bufferSize)
{
using (FileStream fs = File.Open(path, FileMode.Create, FileAccess.Write))
{
int totalBytesSaved = 0;
while (totalBytesSaved<data.Length)
{
int remainingBytes = Math.Min(bufferSize, data.Length - totalBytesSaved);
fs.Write(data, totalBytesSaved, remainingBytes);
totalBytesSaved += remainingBytes;
}
}
}
However, loading entire images into memory will require quite a lot of RAM. I don't know much about XMP headers, but if possible, you should:
- Load only headers in memory
- Manipulate headers in memory
- Write the headers to a new file
- Copy the remaining data from the original file
a source to share