/// <summary> /// Loads a cache item into memory that either contains the raw binary (if its size fits into the limit) or /// just the blob metadata pointing to the blob storage. /// </summary> /// <param name="versionId">Content version id.</param> /// <param name="propertyTypeId">Binary property type id.</param> public BinaryCacheEntity LoadBinaryCacheEntity(int versionId, int propertyTypeId) { var commandText = string.Format(LoadBinaryCacheentityFormatScript, BlobStorage.BinaryCacheSize); using (var cmd = new SqlProcedure { CommandText = commandText }) { cmd.Parameters.Add("@VersionId", SqlDbType.Int).Value = versionId; cmd.Parameters.Add("@PropertyTypeId", SqlDbType.Int).Value = propertyTypeId; cmd.CommandType = CommandType.Text; using (var reader = cmd.ExecuteReader(CommandBehavior.SingleRow | CommandBehavior.SingleResult)) { if (!reader.HasRows || !reader.Read()) { return(null); } var length = reader.GetInt64(0); var binaryPropertyId = reader.GetInt32(1); var fileId = reader.GetInt32(2); var providerName = reader.GetSafeString(3); var providerTextData = reader.GetSafeString(4); byte[] rawData = null; SqlFileStreamData fileStreamData = null; var useFileStream = providerName == null && reader.GetInt32(6) == 1; if (useFileStream) { // fill Filestream info if we really need it fileStreamData = new SqlFileStreamData { Path = reader.GetSafeString(7), TransactionContext = reader.GetSqlBytes(8).Buffer }; } var provider = useFileStream ? new SqlFileStreamBlobProvider() : BlobStorageBase.GetProvider(providerName); var context = new BlobStorageContext(provider, providerTextData) { VersionId = versionId, PropertyTypeId = propertyTypeId, FileId = fileId, Length = length }; if (provider == BlobStorageBase.BuiltInProvider) { context.BlobProviderData = new BuiltinBlobProviderData(); } else if (provider is SqlFileStreamBlobProvider) { context.BlobProviderData = new SqlFileStreamBlobProviderData { FileStreamData = fileStreamData } } ; if (IsBuiltInOrSqlFileStreamProvider(provider)) { if (!reader.IsDBNull(5)) { rawData = (byte[])reader.GetValue(5); } } return(new BinaryCacheEntity { Length = length, RawData = rawData, BinaryPropertyId = binaryPropertyId, FileId = fileId, Context = context }); } } }
public async Task <string> StartChunkAsync(IBlobProvider blobProvider, int versionId, int propertyTypeId, long fullSize, CancellationToken cancellationToken) { var db = DataProvider.DB; // Get related objects var binaryDoc = db.BinaryProperties.FirstOrDefault(r => r.VersionId == versionId && r.PropertyTypeId == propertyTypeId); if (binaryDoc == null) { return(null); } var fileDoc = db.Files.FirstOrDefault(x => x.FileId == binaryDoc.FileId); if (fileDoc == null) { return(null); } if (fileDoc.Staging) { return(null); } // Create context var binaryPropertyId = binaryDoc.BinaryPropertyId; var fileId = fileDoc.FileId; var providerName = fileDoc.BlobProvider; var providerTextData = fileDoc.BlobProviderData; var provider = BlobStorageBase.GetProvider(providerName); var context = new BlobStorageContext(provider, providerTextData) { VersionId = versionId, PropertyTypeId = propertyTypeId, FileId = fileId, Length = fullSize, }; // Allocate a new blob await blobProvider.AllocateAsync(context, cancellationToken); var blobProviderName = blobProvider.GetType().FullName; var blobProviderData = BlobStorageContext.SerializeBlobProviderData(context.BlobProviderData); // Insert a new file row var contentType = fileDoc.ContentType; var fileNameWithoutExtension = fileDoc.FileNameWithoutExtension; var extension = fileDoc.Extension; var newFileId = db.Files.GetNextId(); db.Files.Insert(new FileDoc { FileId = newFileId, BlobProvider = blobProviderName, BlobProviderData = blobProviderData, ContentType = contentType, Extension = extension, FileNameWithoutExtension = fileNameWithoutExtension, Size = fullSize, Staging = true, }); // Return a token return(new ChunkToken { VersionId = versionId, PropertyTypeId = propertyTypeId, BinaryPropertyId = binaryPropertyId, FileId = newFileId }.GetToken()); }
public async Task <BinaryDataValue> LoadBinaryPropertyAsync(int versionId, int propertyTypeId, SnDataContext dataContext) { if (!(dataContext is MsSqlDataContext sqlCtx)) { throw new PlatformNotSupportedException(); } return(await sqlCtx.ExecuteReaderAsync(LoadBinaryPropertyScript, cmd => { cmd.Parameters.AddRange(new[] { sqlCtx.CreateParameter("@VersionId", DbType.Int32, versionId), sqlCtx.CreateParameter("@PropertyTypeId", DbType.Int32, propertyTypeId), }); }, async (reader, cancel) => { cancel.ThrowIfCancellationRequested(); if (!await reader.ReadAsync(cancel).ConfigureAwait(false)) { return null; } var size = reader.GetInt64("Size"); var binaryPropertyId = reader.GetInt32("BinaryPropertyId"); var fileId = reader.GetInt32("FileId"); var providerName = reader.GetSafeString("BlobProvider"); var providerTextData = reader.GetSafeString("BlobProviderData"); var provider = BlobStorageBase.GetProvider(providerName); var context = new BlobStorageContext(provider, providerTextData) { VersionId = versionId, PropertyTypeId = propertyTypeId, FileId = fileId, Length = size }; Stream stream = null; if (provider == BlobStorageBase.BuiltInProvider) { context.BlobProviderData = new BuiltinBlobProviderData(); var streamIndex = reader.GetOrdinal("Stream"); if (!reader.IsDBNull(streamIndex)) { var rawData = (byte[])reader.GetValue(streamIndex); stream = new MemoryStream(rawData); } } return new BinaryDataValue { Id = binaryPropertyId, FileId = fileId, ContentType = reader.GetSafeString("ContentType"), FileName = new BinaryFileName( reader.GetSafeString("FileNameWithoutExtension") ?? "", reader.GetSafeString("Extension") ?? ""), Size = size, Checksum = reader.GetSafeString("Checksum"), BlobProviderName = providerName, BlobProviderData = providerTextData, Timestamp = reader.GetSafeLongFromBytes("Timestamp"), Stream = stream }; }).ConfigureAwait(false)); }
public override int Read(byte[] buffer, int offset, int count) { if (buffer == null) { throw new ArgumentNullException(nameof(buffer)); } if (offset + count > buffer.Length) { throw new ArgumentException("Offset + count must not be greater than the buffer length."); } if (offset < 0) { throw new ArgumentOutOfRangeException(nameof(offset), "The offset must be greater than zero."); } if (count < 0) { throw new ArgumentOutOfRangeException(nameof(count), "The count must be greater than zero."); } // Calculate the maximum count of the bytes that can be read. // Return immediately if nothing to read. var maximumReadableByteCount = Length - Position; if (maximumReadableByteCount < 1) { return(0); } var isLocalTransaction = false; var realCount = (int)Math.Min(count, maximumReadableByteCount); if (CanInnerBufferHandleReadRequest(realCount)) { Array.Copy(_innerBuffer, Position - _innerBufferFirstPostion, buffer, offset, realCount); } else { if (!TransactionScope.IsActive) { // make sure we do not use an obsolete value _fileStreamData = null; // Start a new transaction here to serve the needs of the SqlFileStream type. TransactionScope.Begin(); isLocalTransaction = true; } try { // Load transaction data for SqlFilestream. If this is not a local transaction, // than we will be able to use this data in the future if the client calls // the Read method multiple times and will not have to execute SQL queries // every time. var ctx = BlobStorageBase.GetBlobStorageContext(FileId); if (ctx != null) { if (SqlFileStreamBlobMetaDataProvider.IsBuiltInOrSqlFileStreamProvider(ctx.Provider)) { _fileStreamData = ((SqlFileStreamBlobProviderData)ctx.BlobProviderData).FileStreamData; } } if (_fileStreamData == null) { throw new InvalidOperationException($"Transaction data and file path could not be retrieved for SqlFilestream. FileId: {FileId}. Provider: {ctx?.Provider?.GetType().FullName}"); } using (var fs = new SqlFileStream(_fileStreamData.Path, _fileStreamData.TransactionContext, FileAccess.Read, FileOptions.SequentialScan, 0)) { fs.Seek(Position, SeekOrigin.Begin); _innerBuffer = null; var bytesRead = 0; var bytesStoredInInnerBuffer = 0; while (bytesRead < realCount) { var bytesToReadInThisIteration = (int)Math.Min(Length - Position - bytesRead, BlobStorage.BinaryChunkSize); var bytesToStoreInThisIteration = Math.Min(bytesToReadInThisIteration, realCount - bytesRead); var tempBuffer = new byte[bytesToReadInThisIteration]; // copy the bytes from the file stream to the temp buffer // (it is possible that we loaded a lot more bytes than the client requested) fs.Read(tempBuffer, 0, bytesToReadInThisIteration); // first iteration: create inner buffer for caching a part of the stream in memory if (_innerBuffer == null) { _innerBuffer = new byte[GetInnerBufferSize(realCount)]; _innerBufferFirstPostion = Position; } // store a fragment of the data in the inner buffer if possible if (bytesStoredInInnerBuffer < _innerBuffer.Length) { var bytesToStoreInInnerBuffer = Math.Min(bytesToReadInThisIteration, _innerBuffer.Length - bytesStoredInInnerBuffer); Array.Copy(tempBuffer, 0, _innerBuffer, bytesStoredInInnerBuffer, bytesToStoreInInnerBuffer); bytesStoredInInnerBuffer += bytesToStoreInInnerBuffer; } // copy the chunk from the temp buffer to the buffer of the caller Array.Copy(tempBuffer, 0, buffer, bytesRead, bytesToStoreInThisIteration); bytesRead += bytesToReadInThisIteration; } } } catch { // ReSharper disable once InvertIf if (isLocalTransaction && TransactionScope.IsActive) { TransactionScope.Rollback(); // cleanup isLocalTransaction = false; _fileStreamData = null; } throw; } finally { if (isLocalTransaction && TransactionScope.IsActive) { TransactionScope.Commit(); // Set filestream data to null as this was a local transaction and we cannot use it anymore _fileStreamData = null; } } } Position += realCount; return(realCount); }