public virtual void WriteToFile(string fileName, ID3v2MajorVersion version, EncodingScheme encoding) { FileStream targetFileStream=null; try { // First write all frame data to a memory buffer // This way we catch all fatal exceptions before // touching the file, so there's not a even a slight // chance of corrupting the file. MemoryStream memoryBuffer=new MemoryStream(); WriteFrames(memoryBuffer, version, encoding); int newSize=(int)memoryBuffer.Length; if(newSize==0) { throw new FatalException("No data to write in the tag."); } targetFileStream=File.Open(fileName, FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite); TagHeader oldHeader=TagHeader.FromStream(targetFileStream); bool useIntermediate=false; if(oldHeader==null) { // There is no attached tag in the file. We need to use an intermediate temporary file. useIntermediate=true; // Rewind the place in stream to the very beginning, so that when // we copy the data from the original file to the intermediate file, // we won't lose any data. targetFileStream.Seek(0, SeekOrigin.Begin); } else { // File already has an ID3 v2 tag attached. if(oldHeader.TagSize<newSize) { useIntermediate=true; // Allow for 4KB of padding. newSize+=4*1024; // Move to the correct place in stream so when we copy data to // the intermediate file, we won't lose any data. // The +10 is to go skip the tag header, since it's not included // in TagHeader.TagSize targetFileStream.Seek(oldHeader.TagSize+10, SeekOrigin.Begin); } else { // We should write exactly the same number of bytes back to the file. // When writing the padding, compare memoryBuffer.Length to newSize to // calculate the number of padding bytes required to write. newSize=oldHeader.TagSize; // Seek the beginning of the file. The tag header and frame information // will be overwritten. targetFileStream.Seek(0, SeekOrigin.Begin); } } TagHeader newHeader=new TagHeader((byte)version, 0, TagHeaderFlags.None, newSize); if(useIntermediate) { string intermediateFileName=GetTempIntermediateFileName(fileName); FileStream intermediateStream=null; try { intermediateStream=File.Create(intermediateFileName); newHeader.WriteToStream(intermediateStream); // Write the frame data residing in memory buffer. intermediateStream.Write(memoryBuffer.GetBuffer(), 0, (int)memoryBuffer.Length); //Write any required paddings. for(int i=(int)memoryBuffer.Length;i<newSize;i++) { intermediateStream.WriteByte(0); } // Copy the data from the original file to the intermediate file. CopyFromStreamToStream(targetFileStream, intermediateStream); // Close the stream of original and intermediate file streams. targetFileStream.Close(); intermediateStream.Close(); // If control reaches this point, then everything went file, so // should normally delete the original file and rename the intermediate file. // But as a safety measure, for pre-release, alpha, and beta version, // instead of removing the file, I decided to rename the old file to // fileName+".old."+revisionNumber instead. The user can manually delete // the these files after ensuring the integrity of the new files. #if ACHAMENES_ID3_BACKUP_FILES_BEFORE_MODIFICATION File.Move(fileName, GetBackupFileName(fileName)); #else File.Delete(fileName); #endif File.Move(intermediateFileName, fileName); } finally { if(intermediateStream!=null) { intermediateStream.Close(); } } } else // If using an intermediate file is not necessary. { // Similarly, we would normally just start writing to @stream here, // but instead, we close it, make a backup copy of it, and then // open it again, and write the tag information just to ensure nothing // will be lost. targetFileStream.Close(); #if ACHAMENES_ID3_BACKUP_FILES_BEFORE_MODIFICATION File.Copy(fileName, GetBackupFileName(fileName)); #endif targetFileStream=File.Open(fileName, FileMode.Open, FileAccess.Write, FileShare.Write); // Write the header. newHeader.WriteToStream(targetFileStream); // Write frame data from memory buffer. targetFileStream.Write(memoryBuffer.GetBuffer(), 0, (int)memoryBuffer.Length); // Write any required padding. for(int i=(int)memoryBuffer.Length;i<newSize;i++) { targetFileStream.WriteByte(0); } // And finally close the stream. targetFileStream.Close(); } } finally { if(targetFileStream!=null) { targetFileStream.Close(); } } }
public virtual void WriteToFile(string fileName, ID3v2MajorVersion version, EncodingScheme encoding) { FileStream targetFileStream = null; try { // First write all frame data to a memory buffer // This way we catch all fatal exceptions before // touching the file, so there's not a even a slight // chance of corrupting the file. MemoryStream memoryBuffer = new MemoryStream(); WriteFrames(memoryBuffer, version, encoding); int newSize = (int)memoryBuffer.Length; if (newSize == 0) { throw new FatalException("No data to write in the tag."); } targetFileStream = File.Open(fileName, FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite); TagHeader oldHeader = TagHeader.FromStream(targetFileStream); bool useIntermediate = false; if (oldHeader == null) { // There is no attached tag in the file. We need to use an intermediate temporary file. useIntermediate = true; // Rewind the place in stream to the very beginning, so that when // we copy the data from the original file to the intermediate file, // we won't lose any data. targetFileStream.Seek(0, SeekOrigin.Begin); } else { // File already has an ID3 v2 tag attached. if (oldHeader.TagSize < newSize) { useIntermediate = true; // Allow for 4KB of padding. newSize += 4 * 1024; // Move to the correct place in stream so when we copy data to // the intermediate file, we won't lose any data. // The +10 is to go skip the tag header, since it's not included // in TagHeader.TagSize targetFileStream.Seek(oldHeader.TagSize + 10, SeekOrigin.Begin); } else { // We should write exactly the same number of bytes back to the file. // When writing the padding, compare memoryBuffer.Length to newSize to // calculate the number of padding bytes required to write. newSize = oldHeader.TagSize; // Seek the beginning of the file. The tag header and frame information // will be overwritten. targetFileStream.Seek(0, SeekOrigin.Begin); } } TagHeader newHeader = new TagHeader((byte)version, 0, TagHeaderFlags.None, newSize); if (useIntermediate) { string intermediateFileName = GetTempIntermediateFileName(fileName); FileStream intermediateStream = null; try { intermediateStream = File.Create(intermediateFileName); newHeader.WriteToStream(intermediateStream); // Write the frame data residing in memory buffer. intermediateStream.Write(memoryBuffer.GetBuffer(), 0, (int)memoryBuffer.Length); //Write any required paddings. for (int i = (int)memoryBuffer.Length; i < newSize; i++) { intermediateStream.WriteByte(0); } // Copy the data from the original file to the intermediate file. CopyFromStreamToStream(targetFileStream, intermediateStream); // Close the stream of original and intermediate file streams. targetFileStream.Close(); intermediateStream.Close(); // If control reaches this point, then everything went file, so // should normally delete the original file and rename the intermediate file. // But as a safety measure, for pre-release, alpha, and beta version, // instead of removing the file, I decided to rename the old file to // fileName+".old."+revisionNumber instead. The user can manually delete // the these files after ensuring the integrity of the new files. #if ACHAMENES_ID3_BACKUP_FILES_BEFORE_MODIFICATION File.Move(fileName, GetBackupFileName(fileName)); #else File.Delete(fileName); #endif File.Move(intermediateFileName, fileName); } finally { if (intermediateStream != null) { intermediateStream.Close(); } } } else // If using an intermediate file is not necessary. { // Similarly, we would normally just start writing to @stream here, // but instead, we close it, make a backup copy of it, and then // open it again, and write the tag information just to ensure nothing // will be lost. targetFileStream.Close(); #if ACHAMENES_ID3_BACKUP_FILES_BEFORE_MODIFICATION File.Copy(fileName, GetBackupFileName(fileName)); #endif targetFileStream = File.Open(fileName, FileMode.Open, FileAccess.Write, FileShare.Write); // Write the header. newHeader.WriteToStream(targetFileStream); // Write frame data from memory buffer. targetFileStream.Write(memoryBuffer.GetBuffer(), 0, (int)memoryBuffer.Length); // Write any required padding. for (int i = (int)memoryBuffer.Length; i < newSize; i++) { targetFileStream.WriteByte(0); } // And finally close the stream. targetFileStream.Close(); } } finally { if (targetFileStream != null) { targetFileStream.Close(); } } }