GridFS是一種在MongoDB中存儲大二進制文件的機制。使用GridFS存文件有如下幾個原因:
1、利用Grid可以簡化需求。要是已經用了MongoDB,GridFS就可以不需要使用獨立文件存儲架構。
2、GridFS會直接利用業已建立的複製或分片機制,所以對於文件存儲來說故障恢復和擴展都很容易。
3、GridFS可以避免用於存儲用戶上傳內容的文件系統出現的某些問題。例如,
GridFS在同一個目錄下放置大量的文件是沒有任何問題的。
4、GridFS不產生磁盤碎片,因爲MongoDB分配數據文件空間時以2GB爲一塊。
使用場景:
1) 有大量的上傳圖片(用戶上傳或者系統本身的文件發佈等)
2) 文件的量級處於飛速增長,有可能打到單機操作系統自己的文件系統的查詢性能瓶頸,甚至超過單機硬盤的擴容範圍.
3) 文件的備份(不適用gridfs這種三方也可以做,但是不盡方便),文件系統訪問的故障轉移和修復..
4) 文件的索引,存儲除文件本身以外還需要關聯更多的元數據信息(比如,不僅僅存儲文件,還要保存一些文件的發佈式作者/發佈時間/文件tag屬性等等自定義信息)並且需要索引的。
5) 基於4),對文件的分類模糊,如果採用操作系統的文件系統,文件夾分類關係混亂或者無法分類時
6) 當前系統是基於web的,對圖片的訪問根據url了規則路由的..(如搭配nginx用,讓nginx直接讀取gridfs的文件)
7) 文件尺寸較小,而且衆多,且文件有可能被遷移/刪除等
8)用於存儲和恢復那些超過16M(BSON文件限制)的文件
接口代碼如下:
mongoc_gridfs_t
提供的是MongoDB 的gridfs 文件系統的接口。地理信息文件系統包含gridfs_files
和gridfs_file_lists
以及相關的api。
mongoc_gridfs_t
是非線程安全的。釋放mongoc_gridfs_t
之前需要先釋放mongoc_gridfs_file_t
和mongoc_gridfs_file_list_t
示例代碼如下:
#include <mongoc.h>
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
int main (int argc, char *argv[])
{
mongoc_gridfs_t *gridfs;
mongoc_gridfs_file_t *file;
mongoc_gridfs_file_list_t *list;
mongoc_gridfs_file_opt_t opt = { 0 };
mongoc_client_t *client;
mongoc_stream_t *stream;
bson_t query;
bson_t child;
bson_error_t error;
ssize_t r;
char buf[4096];
mongoc_iovec_t iov;
const char * filename;
const char * command;
if (argc < 2) {
fprintf(stderr, "usage - %s command ...\n", argv[0]);
return 1;
}
mongoc_init();
iov.iov_base = (void *)buf;
iov.iov_len = sizeof buf;
/* connect to localhost client */
client = mongoc_client_new ("mongodb://127.0.0.1:27017");//創建連接
assert(client);//檢查創建連接結果
/* grab a gridfs handle in test prefixed by fs */
gridfs = mongoc_client_get_gridfs (client, "test", "fs", &error);
assert(gridfs);
command = argv[1];
filename = argv[2];
if (strcmp(command, "read") == 0) {//讀取指定文檔
if (argc != 3) {
fprintf(stderr, "usage - %s read filename\n", argv[0]);
return 1;
}
file = mongoc_gridfs_find_one_by_filename(gridfs, filename, &error);
assert(file);
stream = mongoc_stream_gridfs_new (file);
assert(stream);
for (;;) {
r = mongoc_stream_readv (stream, &iov, 1, -1, 0);
assert (r >= 0);
if (r == 0) {
break;
}
if (fwrite (iov.iov_base, 1, r, stdout) != r) {
MONGOC_ERROR ("Failed to write to stdout. Exiting.\n");
exit (1);
}
}
mongoc_stream_destroy (stream);
mongoc_gridfs_file_destroy (file);
} else if (strcmp(command, "list") == 0) {//列舉所有文檔
bson_init (&query);
bson_append_document_begin (&query, "$orderby", -1, &child);
bson_append_int32 (&child, "filename", -1, 1);
bson_append_document_end (&query, &child);
bson_append_document_begin (&query, "$query", -1, &child);
bson_append_document_end (&query, &child);
list = mongoc_gridfs_find (gridfs, &query);
bson_destroy (&query);
while ((file = mongoc_gridfs_file_list_next (list))) {
const char * name = mongoc_gridfs_file_get_filename(file);
printf("%s\n", name ? name : "?");
mongoc_gridfs_file_destroy (file);
}
mongoc_gridfs_file_list_destroy (list);
} else if (strcmp(command, "write") == 0) {//寫文檔
if (argc != 4) {
fprintf(stderr, "usage - %s write filename input_file\n", argv[0]);
return 1;
}
stream = mongoc_stream_file_new_for_path (argv [3], O_RDONLY, 0);
assert (stream);
opt.filename = filename;
file = mongoc_gridfs_create_file_from_stream (gridfs, stream, &opt);
assert(file);
mongoc_gridfs_file_save(file);
mongoc_gridfs_file_destroy(file);
} else {
fprintf(stderr, "Unknown command");
return 1;
}
mongoc_gridfs_destroy (gridfs);
mongoc_client_destroy (client);
mongoc_cleanup ();
return 0;
}