public override int Read(byte[] buffer, int offset, int count) { if (buffer == null) { throw new ArgumentNullException("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("offset", "The offset must be greater than zero."); } if (count < 0) { throw new ArgumentOutOfRangeException("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, (int)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. if (_fileStreamData == null || _fileStreamData.TransactionContext == null) { _fileStreamData = DataProvider.Current.LoadFileStreamData(this.BinaryPropertyId); } if (_fileStreamData == null) { throw new InvalidOperationException("Transaction data and file path could not be retrieved for SqlFilestream"); } using (var fs = new System.Data.SqlTypes.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(this.Length - Position - bytesRead, RepositoryConfiguration.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 { 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); }
// Returns a memory stream of the file, using FILESTREAM technology internal MemoryStream GetFileStream(string path, out string mimeType) { string sql = String.Format(@" SELECT MimeType, [Data].PathName(), GET_FILESTREAM_TRANSACTION_CONTEXT() FROM [{0}] WHERE Path = @path", this.TableName); using (TransactionScope tx = new TransactionScope()) using (SqlConnection con = new SqlConnection(this.ConnectionString)) using (SqlCommand cmd = new SqlCommand(sql, con)) { cmd.Parameters.AddWithValue("@path", path); con.Open(); using (SqlDataReader reader = cmd.ExecuteReader()) { reader.Read(); mimeType = reader.GetFieldValue<string>(0); string filePath = reader.GetFieldValue<string>(1); byte[] txnToken = reader.GetFieldValue<byte[]>(2); reader.Close(); using (SqlFileStream sqlFileStream = new SqlFileStream(filePath, txnToken, FileAccess.Read)) { MemoryStream ms = new MemoryStream(); sqlFileStream.Seek(0, SeekOrigin.Begin); sqlFileStream.CopyTo(ms); tx.Complete(); return ms; } } } }