MongoDB GridFS 튜토리얼

[출처] http://nodeqa.com/extend_ref/2

Introduction

MongoDB 안에 Large Objects 를 저장할 수 있는 파일시스템 입니다. 간단하게 파일을 저장하여 사용할 수 있고, MongoDB가 설치되어 있으면 자매품격으로 따라오는 녀석입니다. 저장방식은 1개의 파일당 MAX 2GB 사이즈까지 저장가능하며, 256k chunk 단위로 나누어져 저장합니다. 이는 chunks collections 에 나누어진 document 으로 저장됩니다. 이와 meta, filename, content type, 다른 옵션들은 files collection 에 저장됩니다. 즉 files collection 과 chunks collections 은 1:n 관계를 가진다.

Spec

GridFS 는 데이타를 저장하기 위해 2개의 collection 구성됩니다.

  • files : metadata 포함
  • chunks : binary chunks (BSON)

GridFS는 하나의 데이타베이스 안에서 네임스페이스를 가질 수 있습니다. 아무것도 지정하지 않은면 기본은 fs 이며, fs.files, fs.chunks Collection이 기본으로 생성됩니다.

2개의 collection 구성

files

files의 document 구조를 알아보겠습니다.

{
  "_id"        : <unspecified>,    // unique ID for this file
  "length"     : data_number,      // size of the file in bytes
  "chunkSize"  : data_number,      // size of each of the chunks.  Default is 256k
  "uploadDate" : data_date,        // date when object first stored
  "md5"        : data_string       // result of running the "filemd5" command on this file's chunks
}

위 내용은 기본적으로 생성되는 필드이며, 아래와 같이 임의로 지정한 여러필드를 추가할 수 있습니다.

{
  "filename" : data_string,               // human name for the file
  "contentType" : data_string,            // valid mime type for the object
  "aliases" : data_array of data_string,  // optional array of alias strings
  "metadata" : data_object,               // anything the user wants to store
}

파일 1개가 저장되면, files collection 에는 1개의 row가 입력됩니다.

chunks

files collection 과 1:n 으로 관계지어지는 collection 입니다.

{
  "_id"      : <unspecified>,         // object id of the chunk in the _chunks collection
  "files_id" : <unspecified>,         // 일명 files.id FK 라고 생각하면 됩니다.
  "n"        : chunk_number,          // 256k 단위 chunk의 순번입니다. (예) 1,2,3
  "data"     : data_binary,           // BSON binary 형태의 데이타입니다. 
}

files.id와 chunks.filesid 는 FK 형식으로 이어지는 구조입니다.

사용용도

제가 사용한 경우는 PostgreSQL 의 각종 로그정보를 Report (html)으로 자세하게 받아볼 수 있는 pgfouine을 사용하는 과정에서 이 데이타를 API 형식으로 확인하는 프로젝트에서 이용되었습니다.

API형식은 http://host/{hostname}/{date} 으로 되어 있어서, 수천대의 서버의 자세한 로그정보를 손쉽게 꺼내어 확인할 수 있었습니다.

Utility mongofiles

MongoDB 설치경로의 bin 디렉토리에 mongofiles 라는 명령어가 있습니다. 이것은 GridFS 에 간단하게 저장 및 삭제, 검색, 목록 등 많은 명령을 할 수 있는 도구입니다.

help

$ mongofiles -h
ERROR: required parameter is missing in 'host'

usage: mongofiles [options] command [gridfs filename]
command:
  one of (list|search|put|get)
  list - list all files.  'gridfs filename' is an optional prefix 
         which listed filenames must begin with.
  search - search all files. 'gridfs filename' is a substring 
           which listed filenames must contain.
  put - add a file with filename 'gridfs filename'
  get - get a file with filename 'gridfs filename'
  delete - delete all files with filename 'gridfs filename'
options:
  --help                  produce help message
  -v [ --verbose ]        be more verbose (include multiple times for more 
                          verbosity e.g. -vvvvv)
  --version               print the program's version and exit
  -h [ --host ] arg       mongo host to connect to ( <set name>/s1,s2 for sets)
  --port arg              server port. Can also use --host hostname:port
  --ipv6                  enable IPv6 support (disabled by default)
  -u [ --username ] arg   username
  -p [ --password ] arg   password
  --dbpath arg            directly access mongod database files in the given 
                          path, instead of connecting to a mongod  server - 
                          needs to lock the data directory, so cannot be used 
                          if a mongod is currently accessing the same path
  --directoryperdb        if dbpath specified, each db is in a separate 
                          directory
  --journal               enable journaling
  -d [ --db ] arg         database to use
  -c [ --collection ] arg collection to use (some commands)
  -l [ --local ] arg      local filename for put|get (default is to use the 
                          same name as 'gridfs filename')
  -t [ --type ] arg       MIME type for put (default is to omit)
  -r [ --replace ]        Remove other files with same name after PUT

TEST

// 파일을 생성합니다.
$ echo "Hello World" > testfile.txt

// testfile.txt를 GridFS에 입력합니다.
[root@nodejs:~]# mongofiles put testfile.txt 
connected to: 127.0.0.1
added file: { _id: ObjectId('501a6bba8714e1003494b283'), filename: "testfile.txt", chunkSize: 262144, uploadDate: new Date(1343908794415), md5: "e59ff97941044f85df5297e1c302d260", length: 12 }
done!

// GridFS에 저장되어 있는 파일 목록을 확인합니다.
$ mongofiles list
connected to: 127.0.0.1
testfile.txt    12

// 로컬에 저장되어 있는 testfile.txt 를 삭제합니다.
$ rm testfile.txt 
rm: remove 일반 파일 `testfile.txt'? y           

// 다시 해당 파일을 GridFS 으로부터 다운로드 하여 복구합니다.
[root@nodejs:~]# mongofiles get testfile.txt
connected to: 127.0.0.1
done write to: testfile.txt

// 다운로드된 파일을 확인합니다.
[root@nodejs:~]# cat testfile.txt 
Hello World

Collection 확인

위의 TEST 과정을 거치면 자동으로 fs.files, fs.chunks collection이 생성됩니다.

$ mongo
MongoDB shell version: 2.0.2
connecting to: test
> use test
switched to db test
> show collections
fs.chunks                  // 확인!
fs.files                   // 확인!
system.indexes

그럼 collections 의 데이타를 확인해보겠습니다.

# 이건가? 

> fs.files.find()
Thu Aug  2 21:11:46 ReferenceError: fs is not defined (shell):1

# 역시 에러네요. 오랜만에 mongodb를 ㅋㅋ 다시한번 

# testfile.txt의 정보를 확인할 수 있습니다.
> db.fs.files.find()
{ "_id" : ObjectId("501a6bba8714e1003494b283"), "filename" : "testfile.txt", "chunkSize" : 262144, "uploadDate" : ISODate("2012-08-02T11:59:54.415Z"), "md5" : "e59ff97941044f85df5297e1c302d260", "length" : 12 }

# n이 0부터 시작되고, data는 바이너리형태인것을 확인합니다.
# 그리고, files_id 는 ObjectId 형식으로 동일한 내용인것을 알 수 있습니다.
> db.fs.chunks.find()
{ "_id" : ObjectId("501a6bba7e0a7ee0226fb956"), "files_id" : ObjectId("501a6bba8714e1003494b283"), "n" : 0, "data" : BinData(0,"SGVsbG8gV29ybGQK") }

추가로 Python 에서 사용했던 소스

pymongo installation

python에서는 pymongo 를 추천합니다.

$ easy_install pymongo
Searching for pymongo
Reading http://cheeseshop.python.org/pypi/pymongo/
Reading http://github.com/mongodb/mongo-python-driver
Reading http://cheeseshop.python.org/pypi/pymongo/2.2.1
Best match: pymongo 2.2.1
Downloading http://pypi.python.org/packages/source/p/pymongo/pymongo-2.2.1.tar.gz#md5=b9e9f844208971f42862d5a205cab1c7
Processing pymongo-2.2.1.tar.gz
Running pymongo-2.2.1/setup.py -q bdist_egg --dist-dir /tmp/easy_install-vTfsvW/pymongo-2.2.1/egg-dist-tmp-uib8Bs
zip_safe flag not set; analyzing archive contents...
Adding pymongo 2.2.1 to easy-install.pth file

Installed /usr/lib/python2.4/site-packages/pymongo-2.2.1-py2.4-linux-x86_64.egg
Processing dependencies for pymongo

대략 이런 클래스

pgfouine 작업할 때 작성한 클래스입니다. 참고하세요.

!/usr/bin/env python
# -*- coding: utf-8 -*-
'''
 * MongoGridFS Wrapper
 *
 * @author nanhapark
'''

# pymongo
from pymongo.connection import Connection
from pymongo.objectid import ObjectId
from gridfs import GridFS

# util
import types

class MongoGridFS:
  def __init__(self): pass
  def connect(self, host, port):
    if isinstance(port, types.StringType) == True:
      port = int(port)

    self.instanceConnection = Connection(host, port)

  def setDB(self, dbname):
    self.db = self.instanceConnection[dbname]

  def setGridFS(self, prefix = 'fs'):
    self.fs = GridFS(self.db, prefix)

  def put(self, data, **kwargs):
    self.fs.put(data, **kwargs)

  def get(self, filename):
    out = self.fs.get_last_version(filename)
    return out.read()


def main():
  # 객체생성
  o = MongoGridFS()

  # 접속
  o.connect('localhost', 27017)

  # db connect
  o.setDB('test')

  # GridFS 객체 생성
  o.setGridFS()

  # hello.txt 파일등록
  o.put('hello.txt', filename="hello.txt")

  # 가져오기
  ret = o.get('hello.txt')
  print ret

if __name__ == '__main__':
  main()

결론

pgfouine 프로젝트시에 어떻게 하면 html을 쉽게 저장 / 분산할 수 있을까 생각하다가, Mongodb GridFS를 알게 되었는데, 거창한 FileSystem 보다는 훨씬 심플하고 장난감(?) 같은 느낌이랄까요? 그럼 이만 감사합니다. KIN플하세요. ~~~

© Copyright Since 2012 nanhapark All Rights reserved.Licensed by nanhapark 크리에이티브 커먼즈 라이선스

+ Recent posts