/// <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
                    });
                }
            }
        }
Пример #2
0
        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);
        }