All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.mongodb.gridfs.GridFSInputFile Maven / Gradle / Ivy

Go to download

The MongoDB Java Driver uber-artifact, containing mongodb-driver, mongodb-driver-core, and bson

There is a newer version: 3.1.0
Show newest version
/*
 * 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 com.mongodb.gridfs;

import com.mongodb.BasicDBObjectBuilder;
import com.mongodb.DBObject;
import com.mongodb.MongoException;
import com.mongodb.util.Util;
import org.bson.types.ObjectId;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Date;

/**
 * 

This class represents a GridFS file to be written to the database. Operations include:

* *
    *
  • Writing data obtained from an InputStream
  • *
  • Getting an OutputStream to stream the data out to
  • *
* * @mongodb.driver.manual core/gridfs/ GridFS */ public class GridFSInputFile extends GridFSFile { /** * Default constructor setting the GridFS file name and providing an input stream containing data to be written to the file. * * @param fs The GridFS connection handle. * @param in Stream used for reading data from. * @param filename Name of the file to be created. * @param closeStreamOnPersist indicate the passed in input stream should be closed once the data chunk persisted */ protected GridFSInputFile( GridFS fs , InputStream in , String filename, boolean closeStreamOnPersist ) { _fs = fs; _in = in; _filename = filename; _closeStreamOnPersist = closeStreamOnPersist; _id = new ObjectId(); _chunkSize = GridFS.DEFAULT_CHUNKSIZE; _uploadDate = new Date(); try { _messageDigester = MessageDigest.getInstance("MD5"); } catch (NoSuchAlgorithmException e) { throw new RuntimeException("No MD5!"); } _messageDigester.reset(); _buffer = new byte[(int) _chunkSize]; } /** * Default constructor setting the GridFS file name and providing an input stream containing data to be written to the file. * * @param fs The GridFS connection handle. * @param in Stream used for reading data from. * @param filename Name of the file to be created. */ protected GridFSInputFile( GridFS fs , InputStream in , String filename ) { this( fs, in, filename, false); } /** * Constructor that only provides a file name, but does not rely on the presence of an {@link java.io.InputStream}. An {@link * java.io.OutputStream} can later be obtained for writing using the {@link #getOutputStream()} method. * * @param fs The GridFS connection handle. * @param filename Name of the file to be created. */ protected GridFSInputFile( GridFS fs , String filename ) { this( fs , null , filename ); } /** * Minimal constructor that does not rely on the presence of an {@link java.io.InputStream}. An {@link java.io.OutputStream} can later * be obtained for writing using the {@link #getOutputStream()} method. * * @param fs The GridFS connection handle. */ protected GridFSInputFile( GridFS fs ) { this( fs , null , null ); } /** * Sets the ID of this GridFS file. * * @param id the file's ID. */ public void setId(final Object id) { _id = id; } /** * Sets the file name on the GridFS entry. * * @param fn * File name. */ public void setFilename( String fn ) { _filename = fn; } /** * Sets the content type (MIME type) on the GridFS entry. * * @param ct * Content type. */ public void setContentType( String ct ) { _contentType = ct; } /** * Set the chunk size. This must be called before saving any data. * * @param chunkSize The size in bytes. */ public void setChunkSize(long chunkSize) { if (_outputStream != null || _savedChunks) return; _chunkSize = chunkSize; _buffer = new byte[(int) _chunkSize]; } /** * Calls {@link GridFSInputFile#save(long)} with the existing chunk size. * * @throws MongoException if there's a problem saving the file. */ @Override public void save() { save( _chunkSize ); } /** * This method first calls saveChunks(long) if the file data has not been saved yet. Then it persists the file entry to GridFS. * * @param chunkSize Size of chunks for file in bytes. * @throws MongoException if there's a problem saving the file. */ public void save( long chunkSize ) { if (_outputStream != null) throw new MongoException( "cannot mix OutputStream and regular save()" ); // note that chunkSize only changes _chunkSize in case we actually save chunks // otherwise there is a risk file and chunks are not compatible if ( ! _savedChunks ) { try { saveChunks( chunkSize ); } catch ( IOException ioe ) { throw new MongoException( "couldn't save chunks" , ioe ); } } super.save(); } /** * Saves all data into chunks from configured {@link java.io.InputStream} input stream to GridFS. * * @return Number of the next chunk. * @throws IOException on problems reading the new entry's {@link java.io.InputStream}. * @throws MongoException * @see com.mongodb.gridfs.GridFSInputFile#saveChunks(long) */ public int saveChunks() throws IOException { return saveChunks( _chunkSize ); } /** * Saves all data into chunks from configured {@link java.io.InputStream} input stream to GridFS. A non-default chunk size can be * specified. This method does NOT save the file object itself, one must call save() to do so. * * @param chunkSize Size of chunks for file in bytes. * @return Number of the next chunk. * @throws IOException on problems reading the new entry's {@link java.io.InputStream}. * @throws MongoException */ public int saveChunks( long chunkSize ) throws IOException { if (_outputStream != null) throw new MongoException( "cannot mix OutputStream and regular save()" ); if ( _savedChunks ) throw new MongoException( "chunks already saved!" ); if ( chunkSize <= 0) { throw new MongoException("chunkSize must be greater than zero"); } if ( _chunkSize != chunkSize ) { _chunkSize = chunkSize; _buffer = new byte[(int) _chunkSize]; } int bytesRead = 0; while ( bytesRead >= 0 ) { _currentBufferPosition = 0; bytesRead = _readStream2Buffer(); _dumpBuffer( true ); } // only finish data, do not write file, in case one wants to change metadata _finishData(); return _currentChunkNumber; } /** * After retrieving this {@link java.io.OutputStream}, this object will be capable of accepting successively written data to the output * stream. To completely persist this GridFS object, you must finally call the {@link java.io.OutputStream#close()} method on the output * stream. Note that calling the save() and saveChunks() methods will throw Exceptions once you obtained the OutputStream. * * @return Writable stream object. */ public OutputStream getOutputStream() { if ( _outputStream == null ) { _outputStream = new MyOutputStream(); } return _outputStream; } /** * Dumps a new chunk into the chunks collection. Depending on the flag, also partial buffers (at the end) are going to be written * immediately. * * @param writePartial Write also partial buffers full. * @throws MongoException */ private void _dumpBuffer( boolean writePartial ) { if ( ( _currentBufferPosition < _chunkSize ) && !writePartial ) { // Bail out, chunk not complete yet return; } if (_currentBufferPosition == 0) { // chunk is empty, may be last chunk return; } byte[] writeBuffer = _buffer; if ( _currentBufferPosition != _chunkSize ) { writeBuffer = new byte[_currentBufferPosition]; System.arraycopy( _buffer, 0, writeBuffer, 0, _currentBufferPosition ); } DBObject chunk = createChunk(_id, _currentChunkNumber, writeBuffer); _fs._chunkCollection.save( chunk ); _currentChunkNumber++; _totalBytes += writeBuffer.length; _messageDigester.update( writeBuffer ); _currentBufferPosition = 0; } /** * Creates a new chunk of this file. Can be over-ridden, if input files need to be split into chunks using a different mechanism. * * @param id the file ID * @param currentChunkNumber the unique id for this chunk * @param writeBuffer the byte array containing the data for this chunk * @return a DBObject representing this chunk. */ protected DBObject createChunk(final Object id, final int currentChunkNumber, final byte[] writeBuffer) { return BasicDBObjectBuilder.start() .add("files_id", id) .add("n", currentChunkNumber) .add("data", writeBuffer).get(); } /** * Reads a buffer full from the {@link java.io.InputStream}. * * @return Number of bytes read from stream. * @throws IOException if the reading from the stream fails. */ private int _readStream2Buffer() throws IOException { int bytesRead = 0; while ( _currentBufferPosition < _chunkSize && bytesRead >= 0 ) { bytesRead = _in.read( _buffer, _currentBufferPosition, (int) _chunkSize - _currentBufferPosition ); if ( bytesRead > 0 ) { _currentBufferPosition += bytesRead; } else if ( bytesRead == 0 ) { throw new RuntimeException( "i'm doing something wrong" ); } } return bytesRead; } /** * Marks the data as fully written. This needs to be called before super.save() */ private void _finishData() { if (!_savedChunks) { _md5 = Util.toHex( _messageDigester.digest() ); _messageDigester = null; _length = _totalBytes; _savedChunks = true; try { if ( _in != null && _closeStreamOnPersist ) _in.close(); } catch (IOException e) { //ignore } } } private final InputStream _in; private boolean _closeStreamOnPersist; private boolean _savedChunks = false; private byte[] _buffer = null; private int _currentChunkNumber = 0; private int _currentBufferPosition = 0; private long _totalBytes = 0; private MessageDigest _messageDigester = null; private OutputStream _outputStream = null; /** * An output stream implementation that can be used to successively write to a GridFS file. */ class MyOutputStream extends OutputStream { @Override public void write( int b ) throws IOException { byte[] byteArray = new byte[1]; byteArray[0] = (byte) (b & 0xff); write( byteArray, 0, 1 ); } @Override public void write( byte[] b , int off , int len ) throws IOException { int offset = off; int length = len; int toCopy = 0; while ( length > 0 ) { toCopy = length; if ( toCopy > _chunkSize - _currentBufferPosition ) { toCopy = (int) _chunkSize - _currentBufferPosition; } System.arraycopy( b, offset, _buffer, _currentBufferPosition, toCopy ); _currentBufferPosition += toCopy; offset += toCopy; length -= toCopy; if ( _currentBufferPosition == _chunkSize ) { _dumpBuffer( false ); } } } /** * Processes/saves all data from {@link java.io.InputStream} and closes the potentially present {@link java.io.OutputStream}. The * GridFS file will be persisted afterwards. */ @Override public void close() { // write last buffer if needed _dumpBuffer( true ); // finish stream _finishData(); // save file obj GridFSInputFile.super.save(); } } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy