internal object GetFileHeader(long startPosition) { if (startPosition < 0) { throw new ArgumentNullException("startPosition"); } //читаем системный заголовок this.BlobStream.Seek(startPosition, SeekOrigin.Begin); byte[] systemHeaderBytes = new byte[BlobStreamAdapter.SystemHeaderLength]; this.BlobStream.Read(systemHeaderBytes, 0, BlobStreamAdapter.SystemHeaderLength); //проверка целостности системного заголовка for (int i = 0; i < SystemHeaderFixedBytes.Length; i++) { if (SystemHeaderFixedBytes[i] != systemHeaderBytes[i]) { throw new Exception(string.Format("Битый заголовок файла (позиция {0}). Не удалось прочитать системный заголовок файла.", i)); } } //целостность системного заголовока не нарушена, значит можно поднять заголовок файла //сразу за фиксированной частью системного заголовка есть 4 байта, в которых указана длина заголовка файла //длина заголовка файла int headerLength = BitConverter.ToInt32(systemHeaderBytes, SystemHeaderFixedBytes.Length); if (headerLength < 1) { throw new Exception(string.Format("Не удалось считать длину заголовка файла")); } byte[] headerBytes = new byte[headerLength]; this.BlobStream.Seek(startPosition + BlobStreamAdapter.SystemHeaderLength, SeekOrigin.Begin); this.BlobStream.Read(headerBytes, 0, headerBytes.Length); int headerVersionNumber = BitConverter.ToInt32(systemHeaderBytes, SystemHeaderFixedBytes.Length + BlobConsts.BlobFile.HeaderSizeBytesLength + BlobConsts.BlobFile.AllHashBytesLength); object header = null; using (MemoryStream headerStream = new MemoryStream(headerBytes)) { headerStream.Position = 0; JsonBlobFileHeaderV1 typedHeader = JsonDataSerializer.Deserialize <JsonBlobFileHeaderV1>(headerStream); typedHeader.HeaderLength = headerLength; if (typedHeader.ContentLength == 0) { //длина на момент записи заголовка не была вычислина (потоковая передача) //смотрим эту длину по системному заголовку long contentLengBytesAbsolutePosition = FileStructure.GetContentLengBytesAbsolutePosition(); typedHeader.ContentLength = BitConverter.ToInt64(systemHeaderBytes, (int)contentLengBytesAbsolutePosition); } header = typedHeader; } return(header); }
private object GetFileHeader(byte[] contentWithHeadersData) { if (contentWithHeadersData == null) { throw new ArgumentNullException("fileData"); } object header = null; //сразу за системным заголовком 4 байта, в которых указана длина заголовка файла //длина заголовка файла int headerLength = BitConverter.ToInt32(contentWithHeadersData, SystemHeaderFixedBytes.Length); if (headerLength < 1) { throw new Exception(string.Format("Не удалось считать длину заголовка файла")); } byte[] headerBytes = new byte[headerLength]; Array.Copy(contentWithHeadersData, BlobStreamAdapter.SystemHeaderLength, headerBytes, 0, headerLength); int headerVersionNumber = BitConverter.ToInt32(contentWithHeadersData, SystemHeaderFixedBytes.Length + BlobConsts.BlobFile.HeaderSizeBytesLength + BlobConsts.BlobFile.AllHashBytesLength); using (MemoryStream headerStream = new MemoryStream(headerBytes)) { headerStream.Position = 0; JsonBlobFileHeaderV1 typedHeader = JsonDataSerializer.Deserialize <JsonBlobFileHeaderV1>(headerStream); typedHeader.HeaderLength = headerLength; if (typedHeader.ContentLength == 0) { //длина на момент записи заголовка не была вычислина (потоковая передача) //смотрим эту длину по системному заголовку long contentLengBytesAbsolutePosition = FileStructure.GetContentLengBytesAbsolutePosition(); typedHeader.ContentLength = BitConverter.ToInt64(contentWithHeadersData, (int)contentLengBytesAbsolutePosition); } header = typedHeader; } return(header); }
/// <summary> /// Сохраняет содержимое файла с заголовками в поток. /// </summary> /// <param name="fileMetadata">Метаданные файла.</param> /// <param name="stream">Содержимое файла.</param> /// <param name="versionTimeCreated">Дата создания версии файла.</param> internal void Write(IBlobFileMetadata fileMetadata, Stream stream, DateTime versionTimeCreated) { if (fileMetadata == null) { throw new ArgumentNullException("fileMetadata"); } if (fileMetadata.UniqueID == Guid.Empty) { throw new ArgumentNullException("fileMetadata.UniqueID"); } if (string.IsNullOrEmpty(fileMetadata.Name)) { throw new ArgumentNullException("fileMetadata.Name"); } if (stream == null) { throw new ArgumentNullException("stream"); } if (!this.BlobStream.CanWrite) { throw new Exception(string.Format("Для сохранения файла в поток необходимо передать поток с правами на запись")); } bool contentLengthCalculated = false; long contentLength = 0; if (stream.CanSeek) { contentLength = stream.Length; contentLengthCalculated = true; } byte[] systemHeaderInfoBytes = new byte[BlobStreamAdapter.SystemHeaderLength]; byte[] headerBytes = null; int headerLength = 0; //запись текущей версии заголовка long blobOffset = this.BlobStream.Position; using (MemoryStream fileHeaderStream = new MemoryStream()) { //сериализация заголовка this.SerializeHeader(fileHeaderStream, fileMetadata, contentLength, versionTimeCreated); //после сериализации заголовка становится известна длина headerLength = (int)fileHeaderStream.Length; headerBytes = new byte[headerLength]; fileHeaderStream.Position = 0; fileHeaderStream.Read(headerBytes, 0, headerLength); } //хеш заголовка и содерижмого byte[] headerHash = this.HashAlgorithm.ComputeHash(headerBytes); //длина заголовка byte[] headerLengthBytes = BitConverter.GetBytes(headerLength); //номер версии заголовка byte[] headerVersionBytes = BitConverter.GetBytes(BlobConsts.BlobFile.FileHeaderCurrentVersion); //заполнение системного заголовка //а - фиксированные байты int headerCursor = 0; Array.Copy(SystemHeaderFixedBytes, 0, systemHeaderInfoBytes, headerCursor, SystemHeaderFixedBytes.Length); headerCursor += SystemHeaderFixedBytes.Length; //б - размер заголовка файла Array.Copy(headerLengthBytes, 0, systemHeaderInfoBytes, headerCursor, headerLengthBytes.Length); headerCursor += headerLengthBytes.Length; //в - хеш заголовка файла Array.Copy(headerHash, 0, systemHeaderInfoBytes, headerCursor, headerHash.Length); headerCursor += headerHash.Length; //г - хеш содержимого файла long tmpcontentHashPosition = this.BlobStream.Position + headerCursor; //абсолютная позиция в файле байтов длины содержимого //пустой хеш содержимого, рассчитывается после записи byte[] contentHash = new byte[16]; Array.Copy(contentHash, 0, systemHeaderInfoBytes, headerCursor, contentHash.Length); headerCursor += contentHash.Length; //д - версия заголовка файла Array.Copy(headerVersionBytes, 0, systemHeaderInfoBytes, headerCursor, headerVersionBytes.Length); headerCursor += headerVersionBytes.Length; //е - размер содержимого файла long tmpContentSizePosition = this.BlobStream.Position + headerCursor; //абсолютная позиция в файле байтов длины содержимого byte[] contentLengthBytes = BitConverter.GetBytes(contentLength); Array.Copy(contentLengthBytes, 0, systemHeaderInfoBytes, headerCursor, contentLengthBytes.Length); headerCursor += contentLengthBytes.Length; //1 - запись системного заголовка this.BlobStream.Write(systemHeaderInfoBytes, 0, systemHeaderInfoBytes.Length); //2 - запись заголовка файла this.BlobStream.Write(headerBytes, 0, headerBytes.Length); //3 - запись содержимого long startContentPosition = this.BlobStream.Position; int readBufferSize = 4 * 1024; byte[] buffer = new byte[readBufferSize]; int read = 0; HashAlgorithm tmpHashAlgorithm = this.CreateHashAlgorithm(); byte[] writeBuffer = new byte[readBufferSize]; int writePosition = 0; while ((read = stream.Read(buffer, 0, buffer.Length)) > 0) { tmpHashAlgorithm.TransformBlock(buffer, 0, read, null, 0); if ((writePosition + read) >= writeBuffer.Length) { //дозаполняем буфер int restBufferSize = writeBuffer.Length - writePosition; Array.Copy(buffer, 0, writeBuffer, writePosition, restBufferSize); writePosition += restBufferSize; //скидываем буфер записи на диск this.BlobStream.Write(writeBuffer, 0, writePosition); int newSessionPosition = read - restBufferSize; if (newSessionPosition > 0) { //остальные данные опять загоняем в буфер Array.Copy(buffer, restBufferSize, writeBuffer, 0, newSessionPosition); } writePosition = newSessionPosition; } else { //записываем только в буфер, на диск не скидываем Array.Copy(buffer, 0, writeBuffer, writePosition, read); writePosition += read; } } if (writePosition > 0) { this.BlobStream.Write(writeBuffer, 0, writePosition); } tmpHashAlgorithm.TransformFinalBlock(buffer, 0, read); //физически записанная длина файла long endContentPosition = this.BlobStream.Position; contentLength = endContentPosition - startContentPosition; //4 - переписываем 16 пустых байт хеша содержимого, после его вычисления contentHash = tmpHashAlgorithm.Hash; long contentHashBytesAbsolutePosition = FileStructure.GetContentHashBytesAbsolutePosition(blobOffset); this.BlobStream.Seek(contentHashBytesAbsolutePosition, SeekOrigin.Begin); this.BlobStream.Write(contentHash, 0, contentHash.Length); //5 - в 100 зарезервированных байт пишем длину содержимого (ранее была в заголовке) //теперь обязательно есть в системном заголовке, а в файловом заголовке есть, если пришел буферный поток //делаем это только в случае потоковой передачи, если длина известна сразу, то она и запишется сразу if (!contentLengthCalculated) { long contentLengBytesAbsolutePosition = FileStructure.GetContentLengBytesAbsolutePosition(blobOffset); contentLengthBytes = BitConverter.GetBytes(contentLength); this.BlobStream.Seek(contentLengBytesAbsolutePosition, SeekOrigin.Begin); this.BlobStream.Write(contentLengthBytes, 0, contentLengthBytes.Length); } fileMetadata.Size = contentLength; }