在二进制文件中插入字节

12

我想在图像元数据块的特定标记下添加一些字符串。由于.NET不支持自定义元数据字段,因此必须在字节级别上执行此操作。

该块的结构类似于1C 02 XX YY YY ZZ ZZ ZZ ...,其中XX是我需要追加的字段ID,YY YY是其大小,ZZ=数据。

我想象中应该可以读取到此标记(1C 02 XX)之前的所有图像数据,然后增加大小字节(YY YY),在ZZ的末尾添加数据,然后添加原始文件的其余部分?这正确吗?

我该如何进行?它需要尽可能快地处理4-5 MB的JPEG文件。


http://www.codeproject.com/KB/GDI-plus/ImageInfo.aspx 可能会有用。 - Jim Mischel
3个回答

8
一般情况下无法加速此操作。您必须至少读取需要移动的部分并在更新后的文件中重新编写它。如果可以并行化读取和写入操作,创建新文件并将内容复制到其中可能会更快。
注意:在您的特定情况下,可能无法只在文件中间插入内容,因为大多数文件格式都不是设计用于此类修改。指定您尝试使用的文件格式可能有助于其他人提供更好的方法。

3

使用以下代码解决了这个问题:

            List<byte> dataNew = new List<byte>();
            byte[] data = File.ReadAllBytes(jpegFilePath);

            int j = 0;
            for (int i = 1; i < data.Length; i++)
            {
                if (data[i - 1] == (byte)0x1C) // 1C IPTC
                {
                    if (data[i] == (byte)0x02) // 02 IPTC
                    {
                        if (data[i + 1] == (byte)fileByte) // IPTC field_number, i.e. 0x78 = IPTC_120
                        {
                            j = i;
                            break;
                        }
                    }
                }
            }

            for (int i = 0; i < j + 2; i++) // add data from file before this field
                dataNew.Add(data[i]); 

            int countOld = (data[j + 2] & 255) << 8 | (data[j + 3] & 255); // curr field length
            int countNew = valueToAdd.Length; // new string length
            int newfullSize = countOld + countNew; // sum
            byte[] newSize = BitConverter.GetBytes((Int16)newfullSize); // Int16 on 2 bytes (to use 2 bytes as size)
            Array.Reverse(newSize); // changes order 10 00 to 00 10
            for (int i = 0; i < newSize.Length; i++) // add changed size
                dataNew.Add(newSize[i]);

            for (int i = j + 4; i < j + 4 + countOld; i++) // add old field value
                dataNew.Add(data[i]);

            byte[] newString = ASCIIEncoding.ASCII.GetBytes(valueToAdd);
            for (int i = 0; i < newString.Length; i++) // append with new field value
                dataNew.Add(newString[i]);

            for (int i = j + 4 + newfullSize; i < data.Length; i++) // add rest of the file
                dataNew.Add(data[i]);

            byte[] finalArray = dataNew.ToArray();
            File.WriteAllBytes(Path.Combine(Path.GetDirectoryName(jpegFilePath), "newfile.jpg"), finalArray);                

1
如果文件中有多个1C 02字节,请小心处理。您可以将其包装以仅在8BIM 04 04段内搜索。 - yosh
1
我认为在你的代码末尾没有理由使用MemoryStream或Image,除非它以某种方式重构了你的元数据?你已经拥有了你想要的字节数组,所以只需使用File.WriteAllBytes(path, finalArray)将其写入驱动器即可。 - PolyTekPatrick

1
这里有一个简单而相当快的解决方案。它将给定偏移量后的所有字节根据给定的额外字节数移动到它们的新位置,因此您可以插入您的数据。
public void ExpandFile(FileStream stream, long offset, int extraBytes)
{
  // https://dev59.com/2HA75IYBdhLWcg3w6Nr8
  const int SIZE = 4096;
  var buffer = new byte[SIZE];
  var length = stream.Length;
  // Expand file
  stream.SetLength(length + extraBytes);
  var pos = length;
  int to_read;
  while (pos > offset)
  {
    to_read = pos - SIZE >= offset ? SIZE : (int)(pos - offset);
    pos -= to_read;
    stream.Position = pos;
    stream.Read(buffer, 0, to_read);
    stream.Position = pos + extraBytes;
    stream.Write(buffer, 0, to_read);
  }

需要检查,不过...


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接