me.lightspeed7.mongofs.gridfs.GridFS.java Source code

Java tutorial

Introduction

Here is the source code for me.lightspeed7.mongofs.gridfs.GridFS.java

Source

/*
 * Copyright (c) 2008-2014 MongoDB, Inc.
 * 
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the
 * License. You may obtain a copy of the License at
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS"
 * BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language
 * governing permissions and limitations under the License.
 */

package me.lightspeed7.mongofs.gridfs;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import me.lightspeed7.mongofs.InputFile;

import org.bson.types.ObjectId;

import com.mongodb.BasicDBObject;
import com.mongodb.DB;
import com.mongodb.DBCollection;
import com.mongodb.DBCursor;
import com.mongodb.DBObject;
import com.mongodb.MongoException;

/**
 * Implementation of GridFS - a specification for storing and retrieving files that exceed the BSON-document size limit of 16MB.
 * <p/>
 * Instead of storing a file in a single document, GridFS divides a file into parts, or chunks, and stores each of those chunks as a
 * separate document. By default GridFS limits chunk size to 256k. GridFS uses two collections to store files. One collection stores the
 * file chunks, and the other stores file metadata.
 * <p/>
 * When you query a GridFS store for a file, the driver or client will reassemble the chunks as needed. You can perform range queries on
 * files stored through GridFS. You also can access information from arbitrary sections of files, which allows you to "skip" into the middle
 * of a video or audio file.
 * <p/>
 * GridFS is useful not only for storing files that exceed 16MB but also for storing any files for which you want access without having to
 * load the entire file into memory. For more information on the indications of GridFS, see MongoDB official documentation.
 * 
 */
public class GridFS {

    /**
     * File's chunk size
     */
    public static final int DEFAULT_CHUNKSIZE = 255 * 1024;

    /**
     * Bucket to use for the collection namespaces
     */
    public static final String DEFAULT_BUCKET = "fs";

    /**
     * File's max chunk size
     * 
     * @deprecated You can calculate max chunkSize with a similar formula {@link com.mongodb.MongoClient#getMaxBsonObjectSize()} - 500*1000.
     *             Please ensure that you left enough space for metadata (500kb is enough).
     */
    @Deprecated
    public static final long MAX_CHUNKSIZE = (long) (3.5 * 1000 * 1000);

    private final DB database;
    private final String bucketName;

    private final DBCollection filesCollection;
    private final DBCollection chunksCollection;

    /**
     * Creates a GridFS instance for the default bucket "fs" in the given database. Set the preferred WriteConcern on the give DB with
     * DB.setWriteConcern
     * 
     * @param db
     *            database to work with
     * @throws com.mongodb.MongoException
     * @see com.mongodb.WriteConcern
     */
    public GridFS(final DB db) {

        this(db, DEFAULT_BUCKET);
    }

    /**
     * Creates a GridFS instance for the specified bucket in the given database. Set the preferred WriteConcern on the give DB with
     * DB.setWriteConcern
     * 
     * @param db
     *            database to work with
     * @param bucket
     *            bucket to use in the given database
     * @throws com.mongodb.MongoException
     * @see com.mongodb.WriteConcern
     */
    public GridFS(final DB db, final String bucket) {

        this.database = db;
        this.bucketName = bucket;

        this.filesCollection = database.getCollection(bucketName + ".files");
        this.chunksCollection = database.getCollection(bucketName + ".chunks");

        // ensure standard indexes as long as collections are small
        try {
            if (filesCollection.count() < 1000) {
                filesCollection.createIndex(new BasicDBObject("filename", 1).append("uploadDate", 1));
            }
            if (chunksCollection.count() < 1000) {
                chunksCollection.createIndex(new BasicDBObject("files_id", 1).append("n", 1),
                        new BasicDBObject("unique", true));
            }
        } catch (MongoException e) {
            // TODO: Logging
        }

        filesCollection.setObjectClass(GridFSDBFile.class);
    }

    /**
     * Gets the list of files stored in this gridfs, sorted by filename.
     * 
     * @return cursor of file objects
     */
    public DBCursor getFileList() {

        return filesCollection.find().sort(new BasicDBObject("filename", 1));
    }

    /**
     * Gets a filtered list of files stored in this gridfs, sorted by filename.
     * 
     * @param query
     *            filter to apply
     * @return cursor of file objects
     */
    public DBCursor getFileList(final DBObject query) {

        return filesCollection.find(query).sort(new BasicDBObject("filename", 1));
    }

    /**
     * Gets a sorted, filtered list of files stored in this gridfs.
     * 
     * @param query
     *            filter to apply
     * @param sort
     *            sorting to apply
     * @return cursor of file objects
     */
    public DBCursor getFileList(final DBObject query, final DBObject sort) {

        return filesCollection.find(query).sort(sort);
    }

    /**
     * Finds one file matching the given objectId. Equivalent to findOne(objectId).
     * 
     * @param objectId
     *            the objectId of the file stored on a server
     * @return a gridfs file
     * @throws com.mongodb.MongoException
     */
    public GridFSDBFile find(final ObjectId objectId) {

        return findOne(objectId);
    }

    /**
     * Finds one file matching the given objectId.
     * 
     * @param objectId
     *            the objectId of the file stored on a server
     * @return a gridfs file
     * @throws com.mongodb.MongoException
     */
    public GridFSDBFile findOne(final ObjectId objectId) {

        return findOne(new BasicDBObject("objectId", objectId));
    }

    /**
     * Finds one file matching the given filename.
     * 
     * @param filename
     *            the name of the file stored on a server
     * @return the gridfs db file
     * @throws com.mongodb.MongoException
     */
    public GridFSDBFile findOne(final String filename) {

        return findOne(new BasicDBObject("filename", filename));
    }

    /**
     * Finds one file matching the given query.
     * 
     * @param query
     *            filter to apply
     * @return a gridfs file
     * @throws com.mongodb.MongoException
     */
    public GridFSDBFile findOne(final DBObject query) {

        return injectGridFSInstance(filesCollection.findOne(query));
    }

    /**
     * Finds a list of files matching the given filename.
     * 
     * @param filename
     *            the filename to look for
     * @return list of gridfs files
     * @throws com.mongodb.MongoException
     */
    public List<GridFSDBFile> find(final String filename) {

        return find(new BasicDBObject("filename", filename));
    }

    /**
     * Finds a list of files matching the given filename.
     * 
     * @param filename
     *            the filename to look for
     * @param sort
     *            the fields to sort with
     * @return list of gridfs files
     * @throws com.mongodb.MongoException
     */
    public List<GridFSDBFile> find(final String filename, final DBObject sort) {

        return find(new BasicDBObject("filename", filename), sort);
    }

    /**
     * Finds a list of files matching the given query.
     * 
     * @param query
     *            the filter to apply
     * @return list of gridfs files
     * @throws com.mongodb.MongoException
     */
    public List<GridFSDBFile> find(final DBObject query) {

        return find(query, null);
    }

    /**
     * Finds a list of files matching the given query.
     * 
     * @param query
     *            the filter to apply
     * @param sort
     *            the fields to sort with
     * @return list of gridfs files
     * @throws com.mongodb.MongoException
     */
    public List<GridFSDBFile> find(final DBObject query, final DBObject sort) {

        List<GridFSDBFile> files = new ArrayList<GridFSDBFile>();

        DBCursor cursor = filesCollection.find(query);
        if (sort != null) {
            cursor.sort(sort);
        }

        try {
            while (cursor.hasNext()) {
                files.add(injectGridFSInstance(cursor.next()));
            }
        } finally {
            cursor.close();
        }
        return Collections.unmodifiableList(files);
    }

    private GridFSDBFile injectGridFSInstance(final Object o) {

        if (o == null) {
            return null;
        }

        if (!(o instanceof GridFSDBFile)) {
            throw new IllegalArgumentException("somehow didn't get a GridFSDBFile");
        }

        GridFSDBFile f = (GridFSDBFile) o;
        f.fs = this;
        return f;
    }

    /**
     * Removes the file matching the given id.
     * 
     * @param id
     *            the id of the file to be removed
     * @throws com.mongodb.MongoException
     */
    public void remove(final ObjectId id) {

        if (id == null) {
            throw new IllegalArgumentException("file id can not be null");
        }

        filesCollection.remove(new BasicDBObject("_id", id));
        chunksCollection.remove(new BasicDBObject("files_id", id));
    }

    /**
     * Removes all files matching the given filename.
     * 
     * @param filename
     *            the name of the file to be removed
     * @throws com.mongodb.MongoException
     */
    public void remove(final String filename) {

        if (filename == null) {
            throw new IllegalArgumentException("filename can not be null");
        }

        remove(new BasicDBObject("filename", filename));
    }

    /**
     * Removes all files matching the given query.
     * 
     * @param query
     *            filter to apply
     * @throws com.mongodb.MongoException
     */
    public void remove(final DBObject query) {

        if (query == null) {
            throw new IllegalArgumentException("query can not be null");
        }

        for (final GridFSDBFile f : find(query)) {
            f.remove();
        }
    }

    /**
     * Creates a file entry. After calling this method, you have to call save to persist the file object.
     * 
     * @param data
     *            the file's data
     * @return a gridfs input file
     */
    public GridFSInputFile createFile(final byte[] data) {

        return createFile(new ByteArrayInputStream(data), true);
    }

    /**
     * Creates a file entry. After calling this method, you have to call save to persist the file object.
     * 
     * @param f
     *            the file object
     * @return a gridfs input file
     * @throws IOException
     */
    public InputFile createFile(final File f) throws IOException {

        FileInputStream in = new FileInputStream(f);
        try {
            return createFile(in, f.getName(), true);
        } finally {
            if (in != null) {
                in.close();
            }
        }
    }

    /**
     * Creates a file entry. After calling this method, you have to call save to persist the file object.
     * 
     * @param in
     *            an inputstream containing the file's data
     * @return a gridfs input file
     */
    public InputFile createFile(final InputStream in) {

        return createFile(in, null);
    }

    /**
     * Creates a file entry. After calling this method, you have to call save to persist the file object.
     * 
     * @param in
     *            an inputstream containing the file's data
     * @param closeStreamOnPersist
     *            indicate the passed in input stream should be closed once the data chunk persisted
     * @return a gridfs input file
     */
    public GridFSInputFile createFile(final InputStream in, final boolean closeStreamOnPersist) {

        return createFile(in, null, closeStreamOnPersist);
    }

    /**
     * Creates a file entry. After calling this method, you have to call save to persist the file object.
     * 
     * @param in
     *            an inputstream containing the file's data
     * @param filename
     *            the file name as stored in the db
     * @return a gridfs input file
     */
    public InputFile createFile(final InputStream in, final String filename) {

        return new GridFSInputFile(this, in, filename);
    }

    /**
     * Creates a file entry. After calling this method, you have to call save to persist the file object.
     * 
     * @param in
     *            an inputstream containing the file's data
     * @param filename
     *            the file name as stored in the db
     * @param closeStreamOnPersist
     *            indicate the passed in input stream should be closed once the data chunk persisted
     * @return a gridfs input file
     */
    public GridFSInputFile createFile(final InputStream in, final String filename,
            final boolean closeStreamOnPersist) {

        return new GridFSInputFile(this, in, filename, closeStreamOnPersist);
    }

    /**
     * Creates a file entry.
     * 
     * @param filename
     *            the file name as stored in the db
     * @return a gridfs input file
     * @see GridFS#createFile()
     */
    public GridFSInputFile createFile(final String filename) {

        return new GridFSInputFile(this, filename);
    }

    /**
     * This method creates an empty {@link GridFSInputFile} instance. On this instance an {@link java.io.OutputStream} can be obtained using
     * the {@link GridFSInputFile#getOutputStream()} method. You can still call {@link GridFSInputFile#setContentType(String)} and
     * {@link GridFSInputFile#setFilename(String)}. The file will be completely written and closed after calling the
     * {@link java.io.OutputStream#close()} method on the output stream.
     * 
     * @return GridFS file handle instance.
     */
    public GridFSInputFile createFile() {

        return new GridFSInputFile(this);
    }

    /**
     * Gets the bucket name used in the collection's namespace. Default value is 'fs'.
     * 
     * @return the name of the file bucket
     */
    public String getBucketName() {

        return bucketName;
    }

    /**
     * Gets the database used.
     * 
     * @return the database
     */
    public DB getDB() {

        return database;
    }

    /**
     * Gets the {@link DBCollection} in which the file's metadata is stored.
     * 
     * @return the collection
     */
    /* package */DBCollection getFilesCollection() {

        return filesCollection;
    }

    /**
     * Gets the {@link DBCollection} in which the binary chunks are stored.
     * 
     * @return the collection
     */
    /* package */DBCollection getChunksCollection() {

        return chunksCollection;
    }

}