public IFileVersionMetadata WriteRemoteFile(IFolderMetadata folderMetadata, IRemoteFile remoteFile) { if (folderMetadata == null) { throw new ArgumentNullException("folderMetadata"); } if (remoteFile == null) { throw new ArgumentNullException("remoteFile"); } if (string.IsNullOrEmpty(remoteFile.Name)) { throw new ArgumentNullException("remoteFile.Name"); } if (remoteFile.Stream == null) { throw new ArgumentNullException("remoteFile.Stream"); } IBlobFileMetadata blobFileMetadata = this.BlobMetadataAdapter.CreateFile(folderMetadata, remoteFile.Name); blobFileMetadata.Name = remoteFile.Name; //резервация параметров сохранения файла с blobFileMetadata.EnsureRemoteSaveProperties(remoteFile); //запись файла IFileVersionMetadata savedVersion = this.WriteInternal(blobFileMetadata, remoteFile.Stream, remoteFile.TimeCreated); return(savedVersion); }
public IBlobFileVersionMetadata SaveFile(IBlobFileMetadata fileMetadata, DateTime versionTimeCreated) { if (fileMetadata == null) { throw new ArgumentNullException("fileMetadata"); } if (versionTimeCreated == null) { throw new ArgumentNullException("versionTimeCreated"); } FileMetadata typedFile = (FileMetadata)fileMetadata; if (typedFile.ID != 0 && typedFile.RemoteFile == null) { if (typedFile.OriginalVersionUniqueID == Guid.Empty || fileMetadata.VersionUniqueID == typedFile.OriginalVersionUniqueID) { throw new Exception("Для обновления существующего файла необходимо вызвать резервирование параметров сохранения."); } } this.Logger.WriteMessage("SaveFile: Начало."); FileVersionMetadata version = this.FileAdapter.UpdateFile(typedFile, versionTimeCreated); this.Logger.WriteMessage("SaveFile: Конец."); return(version); }
/// <summary> /// Записывает версию файла в хранилище. /// </summary> /// <param name="fileMetadata">Метаданные существующего файла.</param> /// <param name="stream">Содержимое файла.</param> /// <param name="fileName">Имя файла.</param> /// <returns></returns> public IFileVersionMetadata WriteFileVersion(IFileMetadata fileMetadata, Stream stream, string fileName = null) { if (fileMetadata == null) { throw new ArgumentNullException("fileMetadata"); } if (fileMetadata.FolderMetadata == null) { throw new ArgumentNullException("fileMetadata.FolderMetadata"); } if (stream == null) { throw new ArgumentNullException("stream"); } this.Logger.WriteFormatMessage("WriteFileVersion:Начало записи новой версии файла, fileMetadata.Name: {0}, fileMetadata.UniqueID: {1}", fileMetadata.Name, fileMetadata.UniqueID); IBlobFileMetadata blobFileMetadata = (IBlobFileMetadata)fileMetadata; if (!string.IsNullOrEmpty(fileName)) { blobFileMetadata.Name = fileName; } //резервация параметров сохранения //например идентификатора версии, его нужно знать заранее. blobFileMetadata.EnsureSaveProperties(); //запись версии файла IFileVersionMetadata savedVersion = this.WriteInternal(blobFileMetadata, stream); return(savedVersion); }
private void SerializeHeader(MemoryStream fileHeaderStream, IBlobFileMetadata fileMetadata, long contentLength, DateTime versionTimeCreated) { if (fileHeaderStream == null) { throw new ArgumentNullException("fileHeaderStream"); } if (fileMetadata == null) { throw new ArgumentNullException("fileMetadata"); } //текущая версия заголовка = BlobConsts.BlobFile.FileHeaderCurrentVersion JsonBlobFileHeaderV1 header = new JsonBlobFileHeaderV1() { ContentAbsoluteStartPosition = this.BlobStream.Position, ContentLength = contentLength, FileName = fileMetadata.Name, FolderUrl = fileMetadata.FolderMetadata.Url, UniqueID = fileMetadata.UniqueID, TimeCreated = versionTimeCreated, VersionUniqueID = fileMetadata.VersionUniqueID, }; JsonDataSerializer.Serialize(fileHeaderStream, header); }
/// <summary> /// Удаляет файл. /// </summary> /// <param name="folderUrl">Адрес папки.</param> /// <param name="fileUniqueID">Уникальный идентификатор файла.</param> /// <returns></returns> public bool DeleteFile(IFolderMetadata folderMetadata, Guid fileUniqueID) { if (folderMetadata == null) { throw new ArgumentNullException("folderMetadata"); } if (fileUniqueID == Guid.Empty) { throw new ArgumentNullException("fileUniqueID"); } this.Logger.WriteFormatMessage("DeleteFile:Начало удаления файла, folder.Url: {0}, file.UniqueID: {1}", folderMetadata.Url, fileUniqueID); bool result = false; //физического удаления для файла в блобе не предусмотрено //удаление заключается в установке Deleted для метаданных файла. IBlobFileMetadata fileMetadata = this.BlobMetadataAdapter.GetFile(folderMetadata, fileUniqueID); if (fileMetadata != null) { this.BlobMetadataAdapter.DeleteFile(fileMetadata); result = fileMetadata.Deleted; } this.Logger.WriteFormatMessage("DeleteFile:Окончание удаления файла, folder.Url: {0}, file.UniqueID: {1}", folderMetadata.Url, fileUniqueID); return(result); }
/// <summary> /// Записывает файл в хранилище. /// </summary> /// <param name="folderMetadata">Метаданные папки.</param> /// <param name="fileName">Имя файла.</param> /// <param name="stream">Содержимое файла.</param> /// <returns></returns> public IFileVersionMetadata WriteFile(IFolderMetadata folderMetadata, string fileName, Stream stream) { if (folderMetadata == null) { throw new ArgumentNullException("folderMetadata"); } if (string.IsNullOrEmpty(fileName)) { throw new ArgumentNullException("fileName"); } if (stream == null) { throw new ArgumentNullException("stream"); } IBlobFileMetadata blobFileMetadata = this.BlobMetadataAdapter.CreateFile(folderMetadata, fileName); blobFileMetadata.Name = fileName; //запись файла IFileVersionMetadata savedVersion = this.WriteInternal(blobFileMetadata, stream); return(savedVersion); }
private void EnsureMetadata(IBlobMetadata blobMetadata, IFileHeader fileHeader, long fileAbsoluteStartPosition, long fileAbsoluteEndPosition) { if (blobMetadata == null) { throw new ArgumentNullException("blobMetadata"); } if (fileHeader == null) { throw new ArgumentNullException("fileHeader"); } if (fileAbsoluteStartPosition < 0) { throw new ArgumentNullException("fileAbsolutePosition"); } if (fileAbsoluteEndPosition < 0) { throw new ArgumentNullException("fileAbsoluteEndPosition"); } if (fileAbsoluteStartPosition >= fileAbsoluteEndPosition) { throw new Exception("fileAbsoluteStartPosition не может быть больше fileAbsoluteEndPosition"); } IFolderMetadata folderMetadata = this.DataAdapter.MetadataAdapter.EnsureFolder(fileHeader.FolderUrl); IBlobFileMetadata blobFileMetadata = this.DataAdapter.BlobMetadataAdapter.GetFile(folderMetadata, fileHeader.UniqueID); if (blobFileMetadata != null) { //файл есть, проверяем текущую версию, есть ли она ICollection <IBlobFileVersionMetadata> versions = this.DataAdapter.BlobMetadataAdapter.GetVersions(blobFileMetadata); bool versionExists = false; if (versions != null) { foreach (IBlobFileVersionMetadata version in versions) { if (version.UniqueID == fileHeader.VersionUniqueID) { versionExists = true; break; } } } if (!versionExists) { this.DataAdapter.BlobMetadataAdapter.AddExistsFileVersion(this.DataAdapter.MetadataAdapter.CurrentStorage, blobMetadata, folderMetadata, fileHeader, fileAbsoluteStartPosition, fileAbsoluteEndPosition); } } else { //файла нет, создаем метаданные файла this.DataAdapter.BlobMetadataAdapter.AddExistsFileVersion(this.DataAdapter.MetadataAdapter.CurrentStorage, blobMetadata, folderMetadata, fileHeader, fileAbsoluteStartPosition, fileAbsoluteEndPosition); } }
/// <summary> /// Записывает содержимое файла в блоб. /// </summary> /// <param name="fileMetadata">Матаданные файла.</param> /// <param name="stream">Содержимое файла.</param> /// <param name="remoteTimeCreated">Время создания версии с удаленного узла.</param> /// <returns></returns> internal BlobFileInfo Write(IBlobFileMetadata fileMetadata, Stream stream, DateTime?remoteTimeCreated = null) { if (fileMetadata == null) { throw new ArgumentNullException("fileMetadata"); } if (stream == null) { throw new ArgumentNullException("stream"); } this.Container.DataAdapter.Logger.WriteFormatMessage("Blob.Write:Начало записи содержимого в блоб, file.UniqueID: {0}", fileMetadata.UniqueID); BlobFileInfo blobFileInfo = new BlobFileInfo(); blobFileInfo.BlobID = this.ID; bool closeBlob = false; DateTime timeCreated; if (remoteTimeCreated.HasValue) { timeCreated = remoteTimeCreated.Value; } else { //начиная с 4ой версии хранится время в UTC timeCreated = DateTime.Now.ToUniversalTime(); } using (FileStream fs = this.File.Open(FileMode.Append, FileAccess.Write, FileShare.Read)) { BlobStreamAdapter streamAdapter = new BlobStreamAdapter(fs); blobFileInfo.BlobStartPosition = fs.Length; streamAdapter.Write(fileMetadata, stream, timeCreated); blobFileInfo.BlobEndPosition = fs.Length; blobFileInfo.TimeCreated = timeCreated; closeBlob = fs.Length > this.Container.DataAdapter.MaxBlobSize; } if (closeBlob) { this.Metadata.Closed = true; this.Container.DataAdapter.BlobMetadataAdapter.SaveBlob(this.Metadata); } this.Container.DataAdapter.Logger.WriteFormatMessage("Blob.Write:Окончание записи содержимого в блоб, file.UniqueID: {0}. Блоб был закрыт после записи файла: {1}", fileMetadata.UniqueID, closeBlob); return(blobFileInfo); }
public void DeleteFile(IBlobFileMetadata fileMetadata) { if (fileMetadata == null) { throw new ArgumentNullException("fileMetadata"); } this.Logger.WriteMessage("DeleteFile: Начало."); this.FileAdapter.DeleteFile((FileMetadata)fileMetadata); this.Logger.WriteMessage("DeleteFile: Конец."); }
/// <summary> /// Считывает поток данных файла из блоба. /// </summary> /// <param name="fileMetadata">Метаданные файла.</param> /// <returns></returns> internal Stream ReadStream(IBlobFileMetadata fileMetadata) { if (fileMetadata == null) { throw new ArgumentNullException("fileMetadata"); } //поток закрывает объект, предоставляющий средства потоковой передачи FileStream fs = this.File.Open(FileMode.Open, FileAccess.Read, FileShare.ReadWrite); BlobStreamAdapter streamAdapter = new BlobStreamAdapter(fs); PartitionStream stream = streamAdapter.ReadStream(fileMetadata); return(stream); }
internal byte[] Read(IBlobFileMetadata fileMetadata) { if (fileMetadata == null) { throw new ArgumentNullException("fileMetadata"); } //содержимое файла return(this.ReadContent( fileMetadata.UniqueID, fileMetadata.VersionUniqueID, fileMetadata.BlobStartPosition, fileMetadata.BlobEndPosition)); }
public ICollection <IBlobFileVersionMetadata> GetVersions(IBlobFileMetadata fileMetadata) { if (fileMetadata == null) { throw new ArgumentNullException("fileMetadata"); } this.Logger.WriteFormatMessage("GetVersions: Начало. fileUniqueID='{0}' folderUrl='{1}'", fileMetadata.UniqueID, fileMetadata.FolderMetadata.Url); FileVersionsCollection collection = ((FileMetadata)fileMetadata).Versions; this.Logger.WriteMessage("GetVersions: Конец."); return(collection); }
/// <summary> /// Считывает поток данных файла. /// </summary> /// <param name="fileMetadata">Метаданные файла.</param> /// <returns></returns> public Stream ReadFileStream(IFileMetadata fileMetadata) { if (fileMetadata == null) { throw new ArgumentNullException("fileMetadata"); } this.Logger.WriteFormatMessage("ReadFileStream:Начало чтения содержимого файла, folder.Url: {0}, fileMetadata.UniqueID: {1}", fileMetadata.FolderMetadata.Url, fileMetadata.UniqueID); IBlobFileMetadata typedFileMetadata = (IBlobFileMetadata)fileMetadata; //получаем контейнер блобов Blob blob = this.GetBlob(typedFileMetadata.BlobID); Stream stream = blob.ReadStream(typedFileMetadata); this.Logger.WriteFormatMessage("ReadFileStream:Окончание чтения содержимого файла, folder.Url: {0}, fileMetadata.UniqueID: {1}", fileMetadata.FolderMetadata.Url, fileMetadata.UniqueID); return(stream); }
/// <summary> /// Возвращает метаданные файла хранилища. /// </summary> /// <param name="folderMetadata">Метаданные папки.</param> /// <param name="fileUniqueID">Уникальный идентификатор файла.</param> /// <returns></returns> public IFileMetadata ReadFileMetadata(IFolderMetadata folderMetadata, Guid fileUniqueID) { if (folderMetadata == null) { throw new ArgumentNullException("folderMetadata"); } if (fileUniqueID == Guid.Empty) { throw new ArgumentNullException("fileUniqueID"); } this.Logger.WriteFormatMessage("ReadFileMetadata:Начало получения метаданных файла, folder.Url: {0}, fileUniqueID: {1}", folderMetadata.Url, fileUniqueID); IBlobFileMetadata fileMetadata = this.BlobMetadataAdapter.GetFile(folderMetadata, fileUniqueID); this.Logger.WriteFormatMessage("ReadFileMetadata:Окончание получения метаданных файла, folder.Url: {0}, fileUniqueID: {1}", folderMetadata.Url, fileUniqueID); return(fileMetadata); }
internal PartitionStream ReadStream(IBlobFileMetadata fileMetadata) { if (fileMetadata == null) { throw new ArgumentNullException("fileMetadata"); } if (fileMetadata.BlobStartPosition >= fileMetadata.BlobEndPosition) { throw new Exception(string.Format("Индекс начала файла не может быть равным или больше индекса окончания файла")); } //поток закрывает объект, предоставляющий средства потоковой передачи IBlobFileHeader fileHeader = (IBlobFileHeader)this.GetFileHeader(fileMetadata.BlobStartPosition); long contentStartPosition = fileMetadata.BlobStartPosition + BlobStreamAdapter.SystemHeaderLength + fileHeader.HeaderLength; PartitionStream stream = new PartitionStream(this.BlobStream, contentStartPosition, fileMetadata.Size); return(stream); }
/// <summary> /// Удаляет файл. /// </summary> /// <param name="fileMetadata">Метаданные файла.</param> /// <returns></returns> public bool DeleteFile(IFileMetadata fileMetadata) { if (fileMetadata == null) { throw new ArgumentNullException("fileMetadata"); } if (fileMetadata.FolderMetadata == null) { throw new ArgumentNullException("fileMetadata.FolderMetadata"); } this.Logger.WriteFormatMessage("DeleteFile:Начало удаления файла, folder.Url: {0}, file.UniqueID: {1}", fileMetadata.FolderMetadata.Url, fileMetadata.UniqueID); IBlobFileMetadata typedFileMetadata = (IBlobFileMetadata)fileMetadata; this.BlobMetadataAdapter.DeleteFile(typedFileMetadata); this.Logger.WriteFormatMessage("DeleteFile:Окончание удаления файла, folder.Url: {0}, file.UniqueID: {1}", fileMetadata.FolderMetadata.Url, fileMetadata.UniqueID); return(fileMetadata.Deleted); }
/// <summary> /// Возвращает коллекцию метаданных версий файла. /// </summary> /// <param name="fileMetadata">Метаданные файла.</param> /// <returns></returns> public ICollection <IFileVersionMetadata> ReadFileVersionsMetadata(IFileMetadata fileMetadata) { if (fileMetadata == null) { throw new ArgumentNullException("fileMetadata"); } this.Logger.WriteFormatMessage("ReadFileVersionsMetadata:Начало получения версий файла, folder.Url: {0}, file.UniqueID: {1}", fileMetadata.FolderMetadata.Url, fileMetadata.UniqueID); IBlobFileMetadata typedFileMetadata = (IBlobFileMetadata)fileMetadata; ICollection <IBlobFileVersionMetadata> fileVersionsMetadata = this.BlobMetadataAdapter.GetVersions(typedFileMetadata); List <IFileVersionMetadata> result = new List <IFileVersionMetadata>(); if (fileVersionsMetadata != null) { result.AddRange(fileVersionsMetadata); } this.Logger.WriteFormatMessage("ReadFileVersionsMetadata:Окончание получения версий файла, folder.Url: {0}, file.UniqueID: {1}", fileMetadata.FolderMetadata.Url, fileMetadata.UniqueID); return(result); }
public IFileVersionMetadata WriteRemoteFileVersion(IFileMetadata fileMetadata, IRemoteFile remoteFile) { if (fileMetadata == null) { throw new ArgumentNullException("fileMetadata"); } if (fileMetadata.FolderMetadata == null) { throw new ArgumentNullException("fileMetadata.FolderMetadata"); } if (remoteFile == null) { throw new ArgumentNullException("remoteFile"); } if (remoteFile.Stream == null) { throw new ArgumentNullException("remoteFile.Stream"); } this.Logger.WriteFormatMessage("WriteFileVersion:Начало записи новой версии файла, fileMetadata.Name: {0}, fileMetadata.UniqueID: {1}", fileMetadata.Name, fileMetadata.UniqueID); IBlobFileMetadata blobFileMetadata = (IBlobFileMetadata)fileMetadata; if (!string.IsNullOrEmpty(remoteFile.Name)) { blobFileMetadata.Name = remoteFile.Name; } //резервация параметров сохранения blobFileMetadata.EnsureRemoteSaveProperties(remoteFile); //запись версии файла IFileVersionMetadata savedVersion = this.WriteInternal(blobFileMetadata, remoteFile.Stream, remoteFile.TimeCreated); return(savedVersion); }
private IFileVersionMetadata WriteInternal(IBlobFileMetadata blobFileMetadata, Stream stream, DateTime?remoteTimeCreated = null) { if (blobFileMetadata == null) { throw new ArgumentNullException("param"); } if (stream == null) { throw new ArgumentNullException("stream"); } this.Logger.WriteFormatMessage("WriteFile:Начало записи файла, folderMetadata.Name: {0}, fileName: {1}", blobFileMetadata.FolderMetadata.Name, blobFileMetadata.Name); //получение контейнера блобов. BlobContainer container = this.GetBlobContainer(blobFileMetadata.FolderMetadata); //запись файла в контейнер. BlobFileInfo blobFileInfo = container.Write(blobFileMetadata, stream, remoteTimeCreated); //установка свойств файла, хранящегося в блобе blobFileMetadata.BlobID = blobFileInfo.BlobID; blobFileMetadata.BlobStartPosition = blobFileInfo.BlobStartPosition; blobFileMetadata.BlobEndPosition = blobFileInfo.BlobEndPosition; this.Logger.WriteMessage("WriteFile:Начало сохранения метаданных файла"); IBlobFileVersionMetadata savedVersion = this.BlobMetadataAdapter.SaveFile(blobFileMetadata, blobFileInfo.TimeCreated); this.Logger.WriteMessage("WriteFile:Окончание сохранения метаданных файла"); this.Logger.WriteFormatMessage("WriteFile:Начало записи файла, folderMetadata.Name: {0}, fileName: {1}, fileUniqueID: {2}, fileVersionUniqueID: {3}", blobFileMetadata.FolderMetadata.Name, blobFileMetadata.Name, savedVersion.FileMetadata.UniqueID, savedVersion.UniqueID); return(savedVersion); }
/// <summary> /// Возвращает содержимое файла. /// </summary> /// <param name="fileMetadata">Метаданные файла.</param> /// <returns></returns> internal byte[] Read(IBlobFileMetadata fileMetadata) { if (fileMetadata == null) { throw new ArgumentNullException("fileMetadata"); } if (fileMetadata.BlobStartPosition < 0) { throw new ArgumentNullException("fileMetadata.BlobStartPosition"); } if (fileMetadata.BlobEndPosition < 1) { throw new ArgumentNullException("fileMetadata.BlobEndPosition"); } this.Container.DataAdapter.Logger.WriteFormatMessage("Blob.Read:Начало чтения содержимого из блоба, blobStartPosition: {0}, blobEndPosition: {1}", fileMetadata.BlobStartPosition, fileMetadata.BlobEndPosition); //для операции чтения блоб должен всегда существовать if (!this.File.Exists) { throw new Exception(string.Format("Не удалось найти блоб по пути {0}", this.File.FullName)); } byte[] content = null; using (FileStream fs = this.File.Open(FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) { BlobStreamAdapter streamAdapter = new BlobStreamAdapter(fs); content = streamAdapter.Read(fileMetadata); } this.Container.DataAdapter.Logger.WriteFormatMessage("Blob.Read:Окончание чтения содержимого из блоба, blobStartPosition: {0}, blobEndPosition: {1}", fileMetadata.BlobStartPosition, fileMetadata.BlobEndPosition); return(content); }
/// <summary> /// Записывает файл на физический носитель. /// </summary> /// <param name="fileMetadata">Метаданные файла.</param> /// <param name="stream">Содержимое файла.</param> /// <param name="remoteTimeCreated">Время создания версии с удаленного узла.</param> /// <returns></returns> public BlobFileInfo Write(IBlobFileMetadata fileMetadata, Stream stream, DateTime?remoteTimeCreated = null) { if (fileMetadata == null) { throw new ArgumentNullException("fileMetadata"); } if (string.IsNullOrEmpty(fileMetadata.Name)) { throw new ArgumentNullException("fileMetadata.Name"); } if (stream == null) { throw new ArgumentNullException("stream"); } this.Logger.WriteFormatMessage("BlobContainer.Write:Начало записи содержимого файла в блоб, file.UniqueID: {0}", fileMetadata.UniqueID); BlobFileInfo blobFileInfo; //блоб, на который была получена блокировка для потоковой передачи Blob streamingLockedBlob = null; try { //текущий блоб для записи файла Blob currentBlob = null; List <Blob> activeBlobs = null; //сначала необходимо определить возможно ли осуществить запись //в потоковом режиме (не исчерпан ли лимит) bool isStreamed = !stream.CanSeek; bool useStreamingLocksLimit = this.DataAdapter.AllowedStreamingLocksLimit != 1; if (isStreamed && useStreamingLocksLimit) { //проверка на то, что хотя бы 1а потоковая передача может пройти this.ValidateStreamingLocks(); //в ситуации, когда одновременно пришло несколько событий потоковой передачи данных //чтобы не лочить все блобы контейнера под запись потоковых файлов и оставить место //под запись буферных файлов делаем проверку на % залоченных под потоковую передачу блобов //потоковая передача данных подразумевает передачу больших файлов object containerLocker = this.GetContainerLocker(ContainerLockerType.StreamingWaiting); lock (containerLocker) { //последняя дата загрузки активных блобов DateTime lastLoadActiveBlobsTime = DateTime.Now; //дата начала ожидания потоковой блокировки текущим потоком DateTime startWaitingStreamingLockTime = DateTime.Now; //вычисляем возможна ли потоковая передача bool limitOverflowed = true; while (limitOverflowed) { //ограничение на продолжительность ожидания блокировки TimeSpan waitingTime = DateTime.Now.Subtract(startWaitingStreamingLockTime); if (waitingTime.TotalMinutes >= BlobConsts.Timeouts.WaitingStreamingLockMinutes) { throw new Exception(string.Format("Истекло время, отведенное на операцию ожидания потоковой блокировки блоба. Дата начала ожидания получения потоковой блокировки {0:dd.MM.yyyy HH:mm:ss}", startWaitingStreamingLockTime)); } //перед очередной операцией проверки лимита на потоковые передачи //чтобы не грузить ЦП усыпляем поток Thread.Sleep(50); bool reloadActiveBlobs = activeBlobs == null || DateTime.Now.Subtract(lastLoadActiveBlobsTime).TotalMinutes >= BlobConsts.Timeouts.ReloadActiveBlobsMinutes; if (!reloadActiveBlobs) { //проверяем, что есть закрытые блобы, в выборке, которую получили ранее foreach (Blob tmpBlob in activeBlobs) { if (tmpBlob.Metadata.Closed) { //среди активных блобов появился закрытый во время текущей сессии //=> необходимо перезагрузить актуальный список. reloadActiveBlobs = true; break; } } } //за время ожидания возмно состав активных блобов изменился if (reloadActiveBlobs) { //выбираем заново список активных блобов activeBlobs = this.GetActiveBlobs(); lastLoadActiveBlobsTime = DateTime.Now; } //кол-во текущих локов, вызванных потоковой передачей //т.к. словарь статический, то lock выше на конейнер недостаточно //поэтому получаем число активных потоковых локов через ф-ию, внутри которой общий lock int containerStreamingLocks = this.GetContainerStreamingLocksCount(activeBlobs); //количество локов, которые будут, если мы заблокируем еще 1 файл текущей передачей int containerStreamingLocksWithNewLock = containerStreamingLocks + 1; double containerStreamingLocksUsage = (double)containerStreamingLocksWithNewLock / this.DataAdapter.ActiveBlobsCount; limitOverflowed = containerStreamingLocksUsage > this.DataAdapter.AllowedStreamingLocksLimit; } //вышли из цикла ожидания, значит освободилась как минимум одна ячейка //для выполнения операции потоковой передачи //сразу занимаем эту ячейку currentBlob = this.GetRandomBlob(activeBlobs); this.AddStreamingLock(currentBlob); streamingLockedBlob = currentBlob; } } else { activeBlobs = this.GetActiveBlobs(); currentBlob = this.GetRandomBlob(activeBlobs); } //т.к. общий лок на static переменную это узкое место для записи в разные файлы //разные диски и т.п., то на каждый файл в статическом словаре создается свой объект синхронизации //таким образом получается, что запись в файл1 НЕ блокирует запись в файл2 object blobFileLocker = this.GetBlobLocker(currentBlob); lock (blobFileLocker) { //запись файла в блоб blobFileInfo = currentBlob.Write(fileMetadata, stream, remoteTimeCreated); } } catch (Exception ex) { string errorMessage = string.Format("BlobContainer.Write:Ошибка записи содержимого файла в блоб, file.UniqueID: {0}, текст ошибки: {1}", fileMetadata.UniqueID, ex); this.Logger.WriteMessage(errorMessage, LogLevel.Error); throw ex; } finally { if (streamingLockedBlob != null) { //за данную операцию была получена блокировка для потоковой передачи. //снимаем ее независимо от успешного результата выполнения операции. this.RemoveStreamingLock(streamingLockedBlob); } } this.Logger.WriteFormatMessage("BlobContainer.Write:Окончание записи содержимого файла в блоб, file.UniqueID: {0}", fileMetadata.UniqueID); return(blobFileInfo); }
/// <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; }