文章目录

  1. 1. 什么是GridFS
  2. 2. 为什么要使用GridFS
  3. 3. GridFS 的两个集合
  4. 4. GridFS 基本操作
    1. 4.1. 上传文件
    2. 4.2. 列出清单
    3. 4.3. 查找文件
    4. 4.4. 下载文件
  5. 5. Java 操作 GridFS
    1. 5.1. 上传文件
    2. 5.2. 查找文件
    3. 5.3. 下载文件
    4. 5.4. 删除文件
  6. 6. 总结

GridFS 基于MongoDB的分布式文件存储系统

什么是GridFS

GridFS是一种将大型文件存储在MongoDB的文件规范。所有官方支持的驱动均实现了GridFS规范。GridFS是MongoDB中的一个内置功能,可以用于存放文件。

为什么要使用GridFS

问题1:我之前将文件上传到文件系统中,比如通过FTP上传到文件服务器。并将文件地址存储到MySQL数据库,我觉得这个方式挺好的呀,为什么要使用GridFS?

答:文件系统到了后期会变的很难管理,同时不利于扩展,此外我想做分布式文件系统也显得不那么容易。而GridFS却正好相反,它基于MongoDB的文件系统,便于管理和扩展。

问题2:普通文件系统对于大文件处理比较麻烦,需要考虑文件系统自身的限制,GridFS提供了什么好方案?

答:GridFS 会将大文件对象分割成多个小的chunk(文件片段),一般为256k/块,每块chunk将作为MongoDB的一个文档(document)被存储在chunks集合中。这一点对于很大的内容文件非常有用。此外,GridFS和MongoDB的分片使得你的文件可以分布到多个服务器上,而且没有增加操作的复杂性。

问题3:我可以直接把文件的二进制流存到MongoDB么?

答: MongoDB默认的文件大小上限为16MB。所以,超过这个大小是有问题的。如果你的文件,比如图片、音频、视频等超过了16MB,那么你就应该使用GridFS。

GridFS 的两个集合

GridFS 使用两个集合来存储一个文件:fs.files与fs.chunks。我们可以使用下面命令来进行查看。

show collections

fs.files集合包含了文件的元数据。我们使用命令来查看

db.fs.files.findOne();

以下是刚上传文件的元数据信息的 fs.files 集合文档

{
    "_id" : ObjectId("57fb553ac7fa9102e0000001"),
    "chunkSize" : 261120,
    "uploadDate" : ISODate("2016-10-10T08:45:50.051Z"),
    "length" : 1.53513e+008,
    "md5" : "9222e097e624800fdd9bfb568169ccad",
    "filename" : "E:/jdk-7u79-linux-x64.tar.gz"
}

fs.chunks集合则存储实际的以256KB尺寸进行分割的文件块。如果你有分片的集合,那么文件块会分布到多台服务器上。

db.fs.chunks.getIndexes();

以下是刚上传文件的fs.chunks 集合文档

{
    "0" : {
        "v" : 1,
        "key" : {
            "_id" : 1
        },
        "name" : "_id_",
        "ns" : "gridfs.fs.chunks"
    },
    "1" : {
        "v" : 1,
        "key" : {
            "files_id" : 1,
            "n" : 1
        },
        "name" : "files_id_1_n_1",
        "ns" : "gridfs.fs.chunks"
    }
}

GridFS 基本操作

上传文件

我们使用GridFS的put命令来存储一个文件。首先,调用MongoDB安装目录下bin的mongofiles.exe工具。然后,打开命令提示符,进入到MongoDB的安装目录的bin目录中,找到mongofiles.exe,并输入下面的代码:

mongofiles.exe -d gridfs put E:\elasticsearch-1.7.0.zip

参数说明,–d 指定数据库gridfs

列出清单

使用以下命令来查看数据库中文件的文档

db.fs.files.find()

返回以下内容

/* 1 */
{
    "_id" : ObjectId("57fb553ac7fa9102e0000001"),
    "chunkSize" : 261120,
    "uploadDate" : ISODate("2016-10-10T08:45:50.051Z"),
    "length" : 1.53513e+008,
    "md5" : "9222e097e624800fdd9bfb568169ccad",
    "filename" : "E:/jdk-7u79-linux-x64.tar.gz"
}

/* 2 */
{
    "_id" : ObjectId("57fb6c49c7fa9135d4000001"),
    "chunkSize" : 261120,
    "uploadDate" : ISODate("2016-10-10T10:24:09.654Z"),
    "length" : 3.17848e+007,
    "md5" : "882013a3d205289dddce22c904d3c476",
    "filename" : "E:\\elasticsearch-1.7.0.zip"
}

查找文件

可以看到 fs.chunks 集合中所有的区块,以下我们得到了文件的 _id 值,我们可以根据这个 _id 获取区块(chunk)的数据。

db.fs.chunks.find({files_id:ObjectId('57fb553ac7fa9102e0000001')});

以上实例中,查询返回了 50 个文档的数据,意味着文件被存储在50个区块中。

下载文件

我们使用GridFS的get命令来下载一个文件。

mongofiles.exe -d gridfs get E:\elasticsearch-1.7.0.zip

Java 操作 GridFS

注意,以下是一个简单的入门案例,不适用于实际场景,实际场景需要考虑异常处理、IO流关闭、日志处理等。
使用的Maven版本

<dependency>
    <groupId>org.mongodb</groupId>
    <artifactId>mongo-java-driver</artifactId>
    <version>3.2.2</version>
</dependency>

公共方法

public MongoDatabase mongoDatabase() throws Exception{
    MongoClient mongoClient = new MongoClient(mongoHost, mongoPort);
    return mongoClient.getDatabase(dbName);
}

上传文件

@Test
public void upload() throws Exception {
    // 获取文件流
    File file = new File("E:/zookeeper-3.4.8.tar.gz");
    InputStream in = new FileInputStream(file);
    // 创建一个容器,传入一个`MongoDatabase`类实例db
    GridFSBucket bucket = GridFSBuckets.create(mongoDatabase());
    // 上传
    ObjectId fileId = bucket.uploadFromStream(UUID.randomUUID().toString(), in);
    System.out.println("上传完成。 文件ID:"+fileId);
}

查找文件

@Test
public void findOne() throws Exception {
    // 获取文件ID
    String objectId = "57fbaffcec773716ecc54ef4";
    // 创建一个容器,传入一个`MongoDatabase`类实例db
    GridFSBucket bucket = GridFSBuckets.create(mongoDatabase());
    // 获取内容
    GridFSFindIterable gridFSFindIterable = bucket.find(Filters.eq("_id", new ObjectId(objectId)));
    GridFSFile gridFSFile = gridFSFindIterable.first();
    System.out.println("filename: " + gridFSFile.getFilename());
}

下载文件

@Test
public void download() throws Exception {
    // 获取文件ID
    String objectId = "57fbaffcec773716ecc54ef4";
    // 获取文件流
    File file = new File("D:/zookeeper-3.4.8.tar.gz");
    // 创建一个容器,传入一个`MongoDatabase`类实例db
    GridFSBucket bucket = GridFSBuckets.create(mongoDatabase());
    // 创建输出流
    OutputStream os = new FileOutputStream(file);
    // 下载
    bucket.downloadToStream(new ObjectId(objectId), os);
    System.out.println("下载完成。");
}

删除文件

@Test
public void delete() throws Exception {
    // 获取文件ID
    String objectId = "57fbaffcec773716ecc54ef4";
    // 创建一个容器,传入一个`MongoDatabase`类实例db
    GridFSBucket bucket = GridFSBuckets.create(mongoDatabase());
    // 删除
    bucket.delete(new ObjectId(objectId));
    System.out.println("删除完成。");
}

总结

GridFS是一种大型文件存储方式之一,有其一定的使用场景,务必根据实际场景选择技术方案。

现在主流的文件存储方案,例如云存储服务,以amazon的s3云服务,七牛云存储都是不错的选择。此外,例如还有阿里开源的tair等键值结构数据的解决方案。

(完)

微信公众号

文章目录

  1. 1. 什么是GridFS
  2. 2. 为什么要使用GridFS
  3. 3. GridFS 的两个集合
  4. 4. GridFS 基本操作
    1. 4.1. 上传文件
    2. 4.2. 列出清单
    3. 4.3. 查找文件
    4. 4.4. 下载文件
  5. 5. Java 操作 GridFS
    1. 5.1. 上传文件
    2. 5.2. 查找文件
    3. 5.3. 下载文件
    4. 5.4. 删除文件
  6. 6. 总结