logo

The database supports native storage of binary data within BSON objects. However, BSON objects in MongoDB are limited in size (4MB older versions, 16MB in v1.71.8, higher limits in the future). The GridFS spec provides a mechanism for transparently dividing a large file among multiple documents. This allows us to efficiently store large objects, and in the case of especially large files, such as videos, permits range operations (e.g., fetching only the first N bytes of a file). 这是官网的一段GridFS的介绍. GridFS是提供了把一个大文件切分成多个document来存储的. 向GridFS中插入一个文件, 默认使用fs.files和fs.chunks两个collection来存储此文件的信息的, 其中fs.files存放了文件的信息, fs.chunks存放了文件的数据.

> db.fs.files.findOne()
{
"_id" : ObjectId("4fbfae0ad417a4f2bc5bc6d1"),
"filename" : "./Makefile",      //文件名
"chunkSize" : 262144,           //chunk的大小, 是固定的, 默认为256*1024
"uploadDate" : ISODate("2012-05-25T16:06:34.794Z"), //上传日期
"md5" : "f9eae9d5987644a537862ca3707ff59d", //文件的md5值
"length" : 130  //文件的长度
}

> db.fs.chunks.findOne()
{
"_id" : ObjectId("4fbfae0a4d460742f1aa76dc"),
"files_id" : ObjectId("4fbfae0ad417a4f2bc5bc6d1"), //对应fs.files中的_id
"n" : 0, //文件的第几个chunk,这里要注意 如果文件大于fs.files中的chunkSize则进行分块, 从0计数
"data" : BinData(0,"QWxsOgoJZysrIG1haW4uY3BwIC1ML3Vzci9sb2NhbC9saWIvIC1JL3Vzci9sb2NhbC9pbmNsdWRlIC1sbW9uZ29jbGllbnQg
LWxib29zdF90aHJlYWQgLWxib29zdF9maWxlc3lzdGVtIC1sYm9vc3RfcHJvZ3JhbV9vcHRpb25zCg==") //文件的二进制流
}

下面看具体测试代码:

int main(int argc, const char** argv) 
{
    DBClientConnection pConn;
    pConn.connect("10.15.107.154:20000");

    GridFS* pGridFs = new GridFS(pConn, "TestGF"); 

#if 1 
    ///< 存储文件
    pGridFs->storeFile("./Makefile");
#endif

#if 0
    ///< 遍历文件
    auto_ptr<DBClientCursor> cursor = pGridFs->list();
    if (cursor->more()) {
        BSONObj obj = cursor->next();
        cout<<obj.toString().data()<<endl;;
    }
#endif

#if 0
    ///< 读取文件
    GridFile file = pGridFs->findFile("./Makefile");
    file.write("./hello");
#endif 

#if 0
    ///< 删除文件
    pGridFs->removeFile("./main.cpp");
#endif

    return 0;
}

从存储文件代码开始:

BSONObj GridFS::storeFile( const char* data , size_t length , const string& remoteName , const 
string& contentType) {
        char const * const end = data + length;  //获取文件末尾指针
        OID id;
        id.init();
        BSONObj idObj = BSON("_id" << id); //生成唯一id
        int chunkNumber = 0;
        while (data < end) {
             //这里_chunkSize = DEFAULT_CHUNK_SIZE = 256 * 1024;
             //根据实际文件大小得到ChunkSize
            int chunkLen = MIN(_chunkSize, (unsigned)(end-data));  
            //这里chunkNumber为文件的第几个chunk
            GridFSChunk c(idObj, chunkNumber, data, chunkLen);
            //向fs.chunks这个collection中插入document
            _client.insert( _chunksNS.c_str() , c._data );
            chunkNumber++;
            data += chunkLen;
        }
           //向fs.files这个collection中插入文件相关信息
        return insertFile(remoteName, id, length, contentType);
    }

GridFS读取文件其实也就是对collection的查询操作. 在读取文件的时候, 在fs.files中根据文件名和最新上传时间找到最新的一个文件记录, 然后取出”_id”, 根据”_id”到fs.chunks中查找”files_id”为”_id”的chunk, 并按”n”进行排序,然后读取chunk中”data”中的数据. 文件删除:

GridFile::GridFile( GridFS * grid , BSONObj obj ) {
        _grid = grid;
        _obj = obj;
    }

    GridFile GridFS::findFile( const string& fileName ) {
        return findFile( BSON( "filename" << fileName ) );
    };

    GridFile GridFS::findFile( BSONObj query ) {
//_client为DBClientBase对象, 去fs.files这个集合去query数据.
        query = BSON("query" << query << "orderby" << BSON("uploadDate" << -1)); 
        // 返回GridFile的对象
        return GridFile( this , _client.findOne( _filesNS.c_str() , query ) );
    }

GridFS进行删除文件也是对fs.files和fs.chunks进行删除操作.
 void GridFS::removeFile( const string& fileName ) {
        auto_ptr<DBClientCursor> files = _client.query( _filesNS , BSON( "filename" << fileName ) );
        while (files->more()) {
            BSONObj file = files->next();
            BSONElement id = file["_id"];
            _client.remove( _filesNS.c_str() , BSON( "_id" << id ) );
            _client.remove( _chunksNS.c_str() , BSON( "files_id" << id ) );
        }
    }

PS:

int chunkLen = MIN(_chunkSize, (unsigned)(end-data));

->通过这行代码可以看出网上说的只能存储大文件是错误的, 因为是可以通过文件大小来分chunk的.

原文地址:http://qr.cc/jaERq0

0 回复
需要 登录 后方可回复, 如果你还没有账号你可以 注册 一个帐号。