summaryrefslogtreecommitdiff
path: root/client/gridfs.cpp
blob: 3c84e6cbcc30fff42024dffd5e21fe80b3bcd85a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
// gridfs.cpp

#include "../stdafx.h"
#include <fcntl.h>

#include "gridfs.h"

#if defined(_WIN32)
#include <io.h>
#endif

#ifndef MIN
#define MIN(a,b) ( (a) < (b) ? (a) : (b) )
#endif

namespace mongo {

    const unsigned DEFAULT_CHUNK_SIZE = 256 * 1024;

    Chunk::Chunk( BSONObj o ){
        _data = o;
    }

    Chunk::Chunk( BSONObj fileObject , int chunkNumber , const char * data , int len ){
        BSONObjBuilder b;
        b.appendAs( fileObject["_id"] , "files_id" );
        b.append( "n" , chunkNumber );
        b.appendBinDataArray( "data" , data , len );
        _data = b.obj();
    }


    GridFS::GridFS( DBClientBase& client , string dbName , string prefix ) : _client( client ) , _dbName( dbName ) , _prefix( prefix ){
        _filesNS = dbName + "." + prefix + ".files";
        _chunksNS = dbName + "." + prefix + ".chunks";


        client.ensureIndex( _filesNS , BSON( "filename" << 1 ) );
        client.ensureIndex( _chunksNS , BSON( "files_id" << 1 << "n" << 1 ) );
    }

    GridFS::~GridFS(){
        
    }
    
    BSONObj GridFS::storeFile( string filename ){
        uassert( "file doesn't exist" , boost::filesystem::exists( filename ) );
        
        gridfs_offset length = boost::filesystem::file_size( filename );
        
        BSONObjBuilder fileObject;
        BSONObj idObj;
        OID id;
        {
            
            id.init();
            fileObject.appendOID( "_id" , &id );

            fileObject << "filename" << filename ;
            massert("large files not yet implemented", length <= 0xffffffff);
            fileObject << "length" << (unsigned) length ;
            fileObject << "chunkSize" << DEFAULT_CHUNK_SIZE ;

            BSONObjBuilder b;
            b.appendOID( "_id" , &id );
            idObj = b.obj();
        }
        
        char buf[DEFAULT_CHUNK_SIZE];
        gridfs_offset pos = 0;
        int fd = open( filename.c_str() , O_RDONLY );
        
        int chunkNumber = 0;
        while ( pos < length ){
            int l = read( fd , buf , MIN( DEFAULT_CHUNK_SIZE , length - pos ) );
            
            Chunk c( idObj , chunkNumber , buf , l );
            _client.insert( _chunksNS.c_str() , c._data );
            
            pos += l;
            chunkNumber++;
        }

        BSONObj res;
        if ( ! _client.runCommand( _dbName.c_str() , BSON( "filemd5" << id << "root" << _prefix ) , res ) )
            throw UserException( "filemd5 failed" );
        
        fileObject.appendAs( res["md5"] , "md5" );

        BSONObj real = fileObject.obj();
        _client.insert( _filesNS.c_str() , real );

        return real;
    }

    GridFile::GridFile( GridFS * grid , BSONObj obj ){
        _grid = grid;
        _obj = obj;
    }
    
    GridFile GridFS::findFile( string filename){ 
        return findFile( BSON( "filename" << filename ) ); 
    };

    GridFile GridFS::findFile( BSONObj query ){
        return GridFile( this , _client.findOne( _filesNS.c_str() , query ) );
    }

    auto_ptr<DBClientCursor> GridFS::list(){
        return _client.query( _filesNS.c_str() , BSONObj() );
    }

    auto_ptr<DBClientCursor> GridFS::list( BSONObj o ){
        return _client.query( _filesNS.c_str() , o );
    }

    Chunk GridFile::getChunk( int n ){
        _exists();
        BSONObjBuilder b;
        b.appendAs( _obj["_id"] , "files_id" );
        b.append( "n" , n );

        BSONObj o = _grid->_client.findOne( _grid->_chunksNS.c_str() , b.obj() );
        assert( ! o.isEmpty() );
        return Chunk(o);
    }

    gridfs_offset GridFile::write( ostream & out ){
        _exists();
        
        const int num = getNumChunks();
        
        for ( int i=0; i<num; i++ ){
            Chunk c = getChunk( i );

            int len;
            const char * data = c.data( len );
            out.write( data , len );
        }

        return getContentLength();
    }
    
    gridfs_offset GridFile::write( string where ){
        ofstream out(where.c_str() , ios::out | ios::binary );
        return write( out );
    }

    void GridFile::_exists(){
        uassert( "doesn't exists" , exists() );
    }
    
}