/// <summary>
        /// Refreshes the GridFS file info from the server.
        /// </summary>
        public void Refresh()
        {
            using (_server.RequestStart(_serverInstance))
            {
                var gridFS          = new MongoGridFS(_server, _databaseName, _settings);
                var database        = gridFS.GetDatabase();
                var filesCollection = gridFS.GetFilesCollection(database);

                MongoCursor <BsonDocument> cursor;
                if (_id != null)
                {
                    cursor = filesCollection.Find(Query.EQ("_id", _id));
                }
                else if (_name != null)
                {
                    cursor = filesCollection.Find(Query.EQ("filename", _name)).SetSortOrder(SortBy.Descending("uploadDate"));
                }
                else
                {
                    throw new InvalidOperationException("Cannot refresh FileInfo when both Id and Name are missing.");
                }
                var fileInfo = cursor.SetLimit(1).FirstOrDefault();
                CacheFileInfo(fileInfo); // fileInfo will be null if file does not exist
            }
        }
        private void UpdateMetadata()
        {
            using (_fileInfo.Server.RequestStart(null, ReadPreference.Primary))
            {
                var gridFS          = new MongoGridFS(_fileInfo.Server, _fileInfo.DatabaseName, _fileInfo.GridFSSettings);
                var database        = gridFS.GetDatabase(ReadPreference.Primary);
                var filesCollection = gridFS.GetFilesCollection(database);

                BsonValue md5 = BsonNull.Value;
                if (_updateMD5)
                {
                    var md5Command = new CommandDocument
                    {
                        { "filemd5", _fileInfo.Id },
                        { "root", gridFS.Settings.Root }
                    };
                    var md5Result = database.RunCommand(md5Command);
                    md5 = md5Result.Response["md5"].AsString;
                }

                var query  = Query.EQ("_id", _fileInfo.Id);
                var update = Update
                             .Set("length", _length)
                             .Set("md5", md5);

                filesCollection.Update(query, update);
            }
        }
 /// <summary>
 /// Moves the most recent version of a GridFS file.
 /// </summary>
 /// <param name="destFileName">The destination file name.</param>
 public void MoveTo(string destFileName)
 {
     EnsureServerInstanceIsPrimary();
     using (_server.RequestStart(_serverInstance))
     {
         var gridFS          = new MongoGridFS(_server, _databaseName, _settings);
         var database        = gridFS.GetDatabase(ReadPreference.Primary);
         var filesCollection = gridFS.GetFilesCollection(database);
         var query           = Query.EQ("_id", _id);
         var update          = Update.Set("filename", destFileName);
         filesCollection.Update(query, update);
     }
 }
        // private methods
        private void AddMissingChunks()
        {
            using (_fileInfo.Server.RequestStart(null, _fileInfo.ServerInstance))
            {
                var gridFS           = new MongoGridFS(_fileInfo.Server, _fileInfo.DatabaseName, _fileInfo.GridFSSettings);
                var database         = gridFS.GetDatabase(ReadPreference.Primary);
                var chunksCollection = gridFS.GetChunksCollection(database);

                var query            = Query.EQ("files_id", _fileInfo.Id);
                var fields           = Fields.Include("n");
                var chunkCount       = (_length + _fileInfo.ChunkSize - 1) / _fileInfo.ChunkSize;
                var chunksFound      = new HashSet <long>();
                var foundExtraChunks = false;
                foreach (var chunk in chunksCollection.Find(query).SetFields(fields))
                {
                    var n = chunk["n"].ToInt64();
                    chunksFound.Add(n);
                    if (n >= chunkCount)
                    {
                        foundExtraChunks = true;
                    }
                }

                if (foundExtraChunks)
                {
                    var extraChunksQuery = Query.And(Query.EQ("files_id", _fileInfo.Id), Query.GTE("n", chunkCount));
                    chunksCollection.Remove(extraChunksQuery);
                }

                BsonBinaryData zeros = null; // delay creating it until it's actually needed
                for (var n = 0L; n < chunkCount; n++)
                {
                    if (!chunksFound.Contains(n))
                    {
                        if (zeros == null)
                        {
                            zeros = new BsonBinaryData(new byte[_fileInfo.ChunkSize]);
                        }
                        var missingChunk = new BsonDocument
                        {
                            { "_id", ObjectId.GenerateNewId() },
                            { "files_id", _fileInfo.Id },
                            { "n", (n < int.MaxValue) ? (BsonValue) new BsonInt32((int)n) : new BsonInt64(n) },
                            { "data", zeros }
                        };
                        chunksCollection.Insert(missingChunk);
                    }
                }
            }
        }
        private void LoadChunk(long chunkIndex)
        {
            if (_chunkIsDirty)
            {
                SaveChunk();
            }

            using (_fileInfo.Server.RequestStart(null, _fileInfo.ServerInstance))
            {
                var gridFS           = new MongoGridFS(_fileInfo.Server, _fileInfo.DatabaseName, _fileInfo.GridFSSettings);
                var database         = gridFS.GetDatabase();
                var chunksCollection = gridFS.GetChunksCollection(database);

                var query    = Query.And(Query.EQ("files_id", _fileInfo.Id), Query.EQ("n", chunkIndex));
                var document = chunksCollection.FindOne(query);
                if (document == null)
                {
                    if (_chunk == null)
                    {
                        _chunk = new byte[_fileInfo.ChunkSize];
                    }
                    else
                    {
                        Array.Clear(_chunk, 0, _chunk.Length);
                    }
                    _chunkId = ObjectId.GenerateNewId();
                }
                else
                {
                    var bytes = document["data"].AsBsonBinaryData.Bytes;
                    if (bytes.Length == _fileInfo.ChunkSize)
                    {
                        _chunk = bytes;
                    }
                    else
                    {
                        if (_chunk == null)
                        {
                            _chunk = new byte[_fileInfo.ChunkSize];
                        }
                        Buffer.BlockCopy(bytes, 0, _chunk, 0, bytes.Length);
                        Array.Clear(_chunk, bytes.Length, _chunk.Length - bytes.Length);
                    }
                    _chunkId = document["_id"];
                }
                _chunkIndex = chunkIndex;
            }
        }
        private void SaveChunk()
        {
            using (_fileInfo.Server.RequestStart(_fileInfo.ServerInstance))
            {
                var connectionId     = _fileInfo.Server.RequestConnectionId;
                var gridFS           = new MongoGridFS(_fileInfo.Server, _fileInfo.DatabaseName, _fileInfo.GridFSSettings);
                var database         = gridFS.GetDatabase(ReadPreference.Primary);
                var chunksCollection = gridFS.GetChunksCollection(database);

                var lastChunkIndex = (_length + _fileInfo.ChunkSize - 1) / _fileInfo.ChunkSize - 1;
                if (_chunkIndex == -1 || _chunkIndex > lastChunkIndex)
                {
                    var message = string.Format("Invalid chunk index {0}.", _chunkIndex);
                    throw new MongoGridFSException(connectionId, message);
                }

                var lastChunkSize = (int)(_length % _fileInfo.ChunkSize);
                if (lastChunkSize == 0)
                {
                    lastChunkSize = _fileInfo.ChunkSize;
                }

                BsonBinaryData data;
                if (_chunkIndex < lastChunkIndex || lastChunkSize == _fileInfo.ChunkSize)
                {
                    data = new BsonBinaryData(_chunk);
                }
                else
                {
                    var lastChunk = new byte[lastChunkSize];
                    Buffer.BlockCopy(_chunk, 0, lastChunk, 0, lastChunkSize);
                    data = new BsonBinaryData(lastChunk);
                }

                var query  = Query.EQ("_id", _chunkId);
                var update = new UpdateDocument
                {
                    { "_id", _chunkId },
                    { "files_id", _fileInfo.Id },
                    { "n", _chunkIndex < int.MaxValue ? (BsonValue)(BsonInt32)(int)_chunkIndex : (BsonInt64)_chunkIndex },
                    { "data", data }
                };
                chunksCollection.Update(query, update, UpdateFlags.Upsert);
                _chunkIsDirty = false;
            }
        }
        /// <summary>
        /// Deletes a GridFS file.
        /// </summary>
        public void Delete()
        {
            EnsureServerInstanceIsPrimary();
            using (_server.RequestStart(_serverInstance))
            {
                var gridFS = new MongoGridFS(_server, _databaseName, _settings);
                gridFS.EnsureIndexes();

                if (Exists)
                {
                    var database         = gridFS.GetDatabase(ReadPreference.Primary);
                    var filesCollection  = gridFS.GetFilesCollection(database);
                    var chunksCollection = gridFS.GetChunksCollection(database);

                    filesCollection.Remove(Query.EQ("_id", _id), gridFS.Settings.WriteConcern);
                    chunksCollection.Remove(Query.EQ("files_id", _id), gridFS.Settings.WriteConcern);
                }
            }
        }
        private void OpenCreate()
        {
            EnsureServerInstanceIsPrimary();
            using (_fileInfo.Server.RequestStart(null, _fileInfo.ServerInstance))
            {
                var gridFS          = new MongoGridFS(_fileInfo.Server, _fileInfo.DatabaseName, _fileInfo.GridFSSettings);
                var database        = gridFS.GetDatabase(ReadPreference.Primary);
                var filesCollection = gridFS.GetFilesCollection(database);

                gridFS.EnsureIndexes();

                _fileIsDirty = true;
                if (_fileInfo.Id == null)
                {
                    _fileInfo.SetId(ObjectId.GenerateNewId());
                }

                var aliases = (_fileInfo.Aliases != null) ? new BsonArray(_fileInfo.Aliases) : null;
                var file    = new BsonDocument
                {
                    { "_id", _fileInfo.Id },
                    { "filename", _fileInfo.Name, !string.IsNullOrEmpty(_fileInfo.Name) },
                    { "length", 0 },
                    { "chunkSize", _fileInfo.ChunkSize },
                    { "uploadDate", _fileInfo.UploadDate },
                    { "md5", BsonNull.Value },                                                              // will be updated when the file is closed (unless UpdateMD5 is false)
                    { "contentType", _fileInfo.ContentType, !string.IsNullOrEmpty(_fileInfo.ContentType) }, // optional
                    { "aliases", aliases, aliases != null },                                                // optional
                    { "metadata", _fileInfo.Metadata, _fileInfo.Metadata != null } // optional
                };
                filesCollection.Insert(file);

                _length   = 0;
                _position = 0;
            }
        }
        private void LoadChunkNoData(long chunkIndex)
        {
            if (_chunkIsDirty)
            {
                SaveChunk();
            }

            using (_fileInfo.Server.RequestStart(null, _fileInfo.ServerInstance))
            {
                var gridFS           = new MongoGridFS(_fileInfo.Server, _fileInfo.DatabaseName, _fileInfo.GridFSSettings);
                var database         = gridFS.GetDatabase();
                var chunksCollection = gridFS.GetChunksCollection(database);

                if (_chunk == null)
                {
                    _chunk = new byte[_fileInfo.ChunkSize];
                }
                else
                {
                    Array.Clear(_chunk, 0, _chunk.Length);
                }

                var query    = Query.And(Query.EQ("files_id", _fileInfo.Id), Query.EQ("n", chunkIndex));
                var fields   = Fields.Include("_id");
                var document = chunksCollection.Find(query).SetFields(fields).SetLimit(1).FirstOrDefault();
                if (document == null)
                {
                    _chunkId = ObjectId.GenerateNewId();
                }
                else
                {
                    _chunkId = document["_id"];
                }
                _chunkIndex = chunkIndex;
            }
        }
        private void UpdateMetadata()
        {
            using (_fileInfo.Server.RequestStart(null, ReadPreference.Primary))
            {
                var gridFS = new MongoGridFS(_fileInfo.Server, _fileInfo.DatabaseName, _fileInfo.GridFSSettings);
                var database = gridFS.GetDatabase(ReadPreference.Primary);
                var filesCollection = gridFS.GetFilesCollection(database);

                BsonValue md5 = BsonNull.Value;
                if (_updateMD5)
                {
                    var md5Command = new CommandDocument
                {
                    { "filemd5", _fileInfo.Id },
                    { "root", gridFS.Settings.Root }
                };
                    var md5Result = database.RunCommand(md5Command);
                    md5 = md5Result.Response["md5"].AsString;
                }

                var query = Query.EQ("_id", _fileInfo.Id);
                var update = Update
                    .Set("length", _length)
                    .Set("md5", md5);

                filesCollection.Update(query, update);
            }
        }
        private void SaveChunk()
        {
            using (_fileInfo.Server.RequestStart(null, _fileInfo.ServerInstance))
            {
                var gridFS = new MongoGridFS(_fileInfo.Server, _fileInfo.DatabaseName, _fileInfo.GridFSSettings);
                var database = gridFS.GetDatabase(ReadPreference.Primary);
                var chunksCollection = gridFS.GetChunksCollection(database);

                var lastChunkIndex = (_length + _fileInfo.ChunkSize - 1) / _fileInfo.ChunkSize - 1;
                if (_chunkIndex == -1 || _chunkIndex > lastChunkIndex)
                {
                    var message = string.Format("Invalid chunk index {0}.", _chunkIndex);
                    throw new MongoGridFSException(message);
                }

                var lastChunkSize = (int)(_length % _fileInfo.ChunkSize);
                if (lastChunkSize == 0)
                {
                    lastChunkSize = _fileInfo.ChunkSize;
                }

                BsonBinaryData data;
                if (_chunkIndex < lastChunkIndex || lastChunkSize == _fileInfo.ChunkSize)
                {
                    data = new BsonBinaryData(_chunk);
                }
                else
                {
                    var lastChunk = new byte[lastChunkSize];
                    Buffer.BlockCopy(_chunk, 0, lastChunk, 0, lastChunkSize);
                    data = new BsonBinaryData(lastChunk);
                }

                var query = Query.EQ("_id", _chunkId);
                var update = new UpdateDocument
                {
                    { "_id", _chunkId },
                    { "files_id", _fileInfo.Id },
                    { "n", (_chunkIndex < int.MaxValue) ? (BsonValue)new BsonInt32((int)_chunkIndex) : new BsonInt64(_chunkIndex) },
                    { "data", data }
                };
                chunksCollection.Update(query, update, UpdateFlags.Upsert);
                _chunkIsDirty = false;
            }
        }
        private void OpenCreate()
        {
            EnsureServerInstanceIsPrimary();
            using (_fileInfo.Server.RequestStart(null, _fileInfo.ServerInstance))
            {
                var gridFS = new MongoGridFS(_fileInfo.Server, _fileInfo.DatabaseName, _fileInfo.GridFSSettings);
                var database = gridFS.GetDatabase(ReadPreference.Primary);
                var filesCollection = gridFS.GetFilesCollection(database);

                gridFS.EnsureIndexes();

                _fileIsDirty = true;
                if (_fileInfo.Id == null)
                {
                    _fileInfo.SetId(ObjectId.GenerateNewId());
                }

                var aliases = (_fileInfo.Aliases != null) ? new BsonArray(_fileInfo.Aliases) : null;
                var file = new BsonDocument
                {
                    { "_id", _fileInfo.Id },
                    { "filename", _fileInfo.Name, !string.IsNullOrEmpty(_fileInfo.Name) },
                    { "length", 0 },
                    { "chunkSize", _fileInfo.ChunkSize },
                    { "uploadDate", _fileInfo.UploadDate },
                    { "md5", BsonNull.Value }, // will be updated when the file is closed (unless UpdateMD5 is false)
                    { "contentType", _fileInfo.ContentType, !string.IsNullOrEmpty(_fileInfo.ContentType) }, // optional
                    { "aliases", aliases, aliases != null }, // optional
                    { "metadata", _fileInfo.Metadata, _fileInfo.Metadata != null } // optional
                };
                filesCollection.Insert(file);

                _length = 0;
                _position = 0;
            }
        }
        private void LoadChunkNoData(long chunkIndex)
        {
            if (_chunkIsDirty) { SaveChunk(); }

            using (_fileInfo.Server.RequestStart(null, _fileInfo.ServerInstance))
            {
                var gridFS = new MongoGridFS(_fileInfo.Server, _fileInfo.DatabaseName, _fileInfo.GridFSSettings);
                var database = gridFS.GetDatabase();
                var chunksCollection = gridFS.GetChunksCollection(database);

                if (_chunk == null)
                {
                    _chunk = new byte[_fileInfo.ChunkSize];
                }
                else
                {
                    Array.Clear(_chunk, 0, _chunk.Length);
                }

                var query = Query.And(Query.EQ("files_id", _fileInfo.Id), Query.EQ("n", chunkIndex));
                var fields = Fields.Include("_id");
                var document = chunksCollection.Find(query).SetFields(fields).SetLimit(1).FirstOrDefault();
                if (document == null)
                {
                    _chunkId = ObjectId.GenerateNewId();
                }
                else
                {
                    _chunkId = document["_id"];
                }
                _chunkIndex = chunkIndex;
            }
        }
        private void LoadChunk(long chunkIndex)
        {
            if (_chunkIsDirty) { SaveChunk(); }

            using (_fileInfo.Server.RequestStart(null, _fileInfo.ServerInstance))
            {
                var gridFS = new MongoGridFS(_fileInfo.Server, _fileInfo.DatabaseName, _fileInfo.GridFSSettings);
                var database = gridFS.GetDatabase();
                var chunksCollection = gridFS.GetChunksCollection(database);

                var query = Query.And(Query.EQ("files_id", _fileInfo.Id), Query.EQ("n", chunkIndex));
                var document = chunksCollection.FindOne(query);
                if (document == null)
                {
                    if (_chunk == null)
                    {
                        _chunk = new byte[_fileInfo.ChunkSize];
                    }
                    else
                    {
                        Array.Clear(_chunk, 0, _chunk.Length);
                    }
                    _chunkId = ObjectId.GenerateNewId();
                }
                else
                {
                    var bytes = document["data"].AsBsonBinaryData.Bytes;
                    if (bytes.Length == _fileInfo.ChunkSize)
                    {
                        _chunk = bytes;
                    }
                    else
                    {
                        if (_chunk == null)
                        {
                            _chunk = new byte[_fileInfo.ChunkSize];
                        }
                        Buffer.BlockCopy(bytes, 0, _chunk, 0, bytes.Length);
                        Array.Clear(_chunk, bytes.Length, _chunk.Length - bytes.Length);
                    }
                    _chunkId = document["_id"];
                }
                _chunkIndex = chunkIndex;
            }
        }
        // private methods
        private void AddMissingChunks()
        {
            using (_fileInfo.Server.RequestStart(null, _fileInfo.ServerInstance))
            {
                var gridFS = new MongoGridFS(_fileInfo.Server, _fileInfo.DatabaseName, _fileInfo.GridFSSettings);
                var database = gridFS.GetDatabase(ReadPreference.Primary);
                var chunksCollection = gridFS.GetChunksCollection(database);

                var query = Query.EQ("files_id", _fileInfo.Id);
                var fields = Fields.Include("n");
                var chunkCount = (_length + _fileInfo.ChunkSize - 1) / _fileInfo.ChunkSize;
                var chunksFound = new HashSet<long>();
                var foundExtraChunks = false;
                foreach (var chunk in chunksCollection.Find(query).SetFields(fields))
                {
                    var n = chunk["n"].ToInt64();
                    chunksFound.Add(n);
                    if (n >= chunkCount)
                    {
                        foundExtraChunks = true;
                    }
                }

                if (foundExtraChunks)
                {
                    var extraChunksQuery = Query.And(Query.EQ("files_id", _fileInfo.Id), Query.GTE("n", chunkCount));
                    chunksCollection.Remove(extraChunksQuery);
                }

                BsonBinaryData zeros = null; // delay creating it until it's actually needed
                for (var n = 0L; n < chunkCount; n++)
                {
                    if (!chunksFound.Contains(n))
                    {
                        if (zeros == null)
                        {
                            zeros = new BsonBinaryData(new byte[_fileInfo.ChunkSize]);
                        }
                        var missingChunk = new BsonDocument
                    {
                        { "_id", ObjectId.GenerateNewId() },
                        { "files_id", _fileInfo.Id },
                        { "n", (n < int.MaxValue) ? (BsonValue)new BsonInt32((int)n) : new BsonInt64(n) },
                        { "data", zeros }
                    };
                        chunksCollection.Insert(missingChunk);
                    }
                }
            }
        }