Coverage Report - org.apache.commons.io.output.DeferredFileOutputStream
 
Classes in this File Line Coverage Branch Coverage Complexity
DeferredFileOutputStream
100%
40/40
100%
12/12
1.8
 
 1  
 /*
 2  
  * Licensed to the Apache Software Foundation (ASF) under one or more
 3  
  * contributor license agreements.  See the NOTICE file distributed with
 4  
  * this work for additional information regarding copyright ownership.
 5  
  * The ASF licenses this file to You under the Apache License, Version 2.0
 6  
  * (the "License"); you may not use this file except in compliance with
 7  
  * the License.  You may obtain a copy of the License at
 8  
  *
 9  
  *      http://www.apache.org/licenses/LICENSE-2.0
 10  
  *
 11  
  * Unless required by applicable law or agreed to in writing, software
 12  
  * distributed under the License is distributed on an "AS IS" BASIS,
 13  
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 14  
  * See the License for the specific language governing permissions and
 15  
  * limitations under the License.
 16  
  */
 17  
 package org.apache.commons.io.output;
 18  
 
 19  
 import java.io.File;
 20  
 import java.io.FileInputStream;
 21  
 import java.io.FileOutputStream;
 22  
 import java.io.IOException;
 23  
 import java.io.OutputStream;
 24  
 
 25  
 import org.apache.commons.io.IOUtils;
 26  
 
 27  
 
 28  
 /**
 29  
  * An output stream which will retain data in memory until a specified
 30  
  * threshold is reached, and only then commit it to disk. If the stream is
 31  
  * closed before the threshold is reached, the data will not be written to
 32  
  * disk at all.
 33  
  * <p>
 34  
  * This class originated in FileUpload processing. In this use case, you do
 35  
  * not know in advance the size of the file being uploaded. If the file is small
 36  
  * you want to store it in memory (for speed), but if the file is large you want
 37  
  * to store it to file (to avoid memory issues).
 38  
  *
 39  
  * @version $Id: DeferredFileOutputStream.java 1471767 2013-04-24 23:24:19Z sebb $
 40  
  */
 41  
 public class DeferredFileOutputStream
 42  
     extends ThresholdingOutputStream
 43  
 {
 44  
 
 45  
     // ----------------------------------------------------------- Data members
 46  
 
 47  
 
 48  
     /**
 49  
      * The output stream to which data will be written prior to the theshold
 50  
      * being reached.
 51  
      */
 52  
     private ByteArrayOutputStream memoryOutputStream;
 53  
 
 54  
 
 55  
     /**
 56  
      * The output stream to which data will be written at any given time. This
 57  
      * will always be one of <code>memoryOutputStream</code> or
 58  
      * <code>diskOutputStream</code>.
 59  
      */
 60  
     private OutputStream currentOutputStream;
 61  
 
 62  
 
 63  
     /**
 64  
      * The file to which output will be directed if the threshold is exceeded.
 65  
      */
 66  
     private File outputFile;
 67  
 
 68  
     /**
 69  
      * The temporary file prefix.
 70  
      */
 71  
     private final String prefix;
 72  
 
 73  
     /**
 74  
      * The temporary file suffix.
 75  
      */
 76  
     private final String suffix;
 77  
 
 78  
     /**
 79  
      * The directory to use for temporary files.
 80  
      */
 81  
     private final File directory;
 82  
 
 83  
 
 84  
     /**
 85  
      * True when close() has been called successfully.
 86  
      */
 87  20
     private boolean closed = false;
 88  
 
 89  
     // ----------------------------------------------------------- Constructors
 90  
 
 91  
 
 92  
     /**
 93  
      * Constructs an instance of this class which will trigger an event at the
 94  
      * specified threshold, and save data to a file beyond that point.
 95  
      *
 96  
      * @param threshold  The number of bytes at which to trigger an event.
 97  
      * @param outputFile The file to which data is saved beyond the threshold.
 98  
      */
 99  
     public DeferredFileOutputStream(final int threshold, final File outputFile)
 100  
     {
 101  12
         this(threshold,  outputFile, null, null, null);
 102  12
     }
 103  
 
 104  
 
 105  
     /**
 106  
      * Constructs an instance of this class which will trigger an event at the
 107  
      * specified threshold, and save data to a temporary file beyond that point.
 108  
      *
 109  
      * @param threshold  The number of bytes at which to trigger an event.
 110  
      * @param prefix Prefix to use for the temporary file.
 111  
      * @param suffix Suffix to use for the temporary file.
 112  
      * @param directory Temporary file directory.
 113  
      *
 114  
      * @since 1.4
 115  
      */
 116  
     public DeferredFileOutputStream(final int threshold, final String prefix, final String suffix, final File directory)
 117  
     {
 118  8
         this(threshold, null, prefix, suffix, directory);
 119  8
         if (prefix == null) {
 120  2
             throw new IllegalArgumentException("Temporary file prefix is missing");
 121  
         }
 122  6
     }
 123  
 
 124  
     /**
 125  
      * Constructs an instance of this class which will trigger an event at the
 126  
      * specified threshold, and save data either to a file beyond that point.
 127  
      *
 128  
      * @param threshold  The number of bytes at which to trigger an event.
 129  
      * @param outputFile The file to which data is saved beyond the threshold.
 130  
      * @param prefix Prefix to use for the temporary file.
 131  
      * @param suffix Suffix to use for the temporary file.
 132  
      * @param directory Temporary file directory.
 133  
      */
 134  
     private DeferredFileOutputStream(final int threshold, final File outputFile, final String prefix, final String suffix, final File directory) {
 135  20
         super(threshold);
 136  20
         this.outputFile = outputFile;
 137  
 
 138  20
         memoryOutputStream = new ByteArrayOutputStream();
 139  20
         currentOutputStream = memoryOutputStream;
 140  20
         this.prefix = prefix;
 141  20
         this.suffix = suffix;
 142  20
         this.directory = directory;
 143  20
     }
 144  
 
 145  
 
 146  
     // --------------------------------------- ThresholdingOutputStream methods
 147  
 
 148  
 
 149  
     /**
 150  
      * Returns the current output stream. This may be memory based or disk
 151  
      * based, depending on the current state with respect to the threshold.
 152  
      *
 153  
      * @return The underlying output stream.
 154  
      *
 155  
      * @exception IOException if an error occurs.
 156  
      */
 157  
     @Override
 158  
     protected OutputStream getStream() throws IOException
 159  
     {
 160  58
         return currentOutputStream;
 161  
     }
 162  
 
 163  
 
 164  
     /**
 165  
      * Switches the underlying output stream from a memory based stream to one
 166  
      * that is backed by disk. This is the point at which we realise that too
 167  
      * much data is being written to keep in memory, so we elect to switch to
 168  
      * disk-based storage.
 169  
      *
 170  
      * @exception IOException if an error occurs.
 171  
      */
 172  
     @Override
 173  
     protected void thresholdReached() throws IOException
 174  
     {
 175  10
         if (prefix != null) {
 176  4
             outputFile = File.createTempFile(prefix, suffix, directory);
 177  
         }
 178  10
         final FileOutputStream fos = new FileOutputStream(outputFile);
 179  10
         memoryOutputStream.writeTo(fos);
 180  10
         currentOutputStream = fos;
 181  10
         memoryOutputStream = null;
 182  10
     }
 183  
 
 184  
 
 185  
     // --------------------------------------------------------- Public methods
 186  
 
 187  
 
 188  
     /**
 189  
      * Determines whether or not the data for this output stream has been
 190  
      * retained in memory.
 191  
      *
 192  
      * @return {@code true} if the data is available in memory;
 193  
      *         {@code false} otherwise.
 194  
      */
 195  
     public boolean isInMemory()
 196  
     {
 197  22
         return !isThresholdExceeded();
 198  
     }
 199  
 
 200  
 
 201  
     /**
 202  
      * Returns the data for this output stream as an array of bytes, assuming
 203  
      * that the data has been retained in memory. If the data was written to
 204  
      * disk, this method returns {@code null}.
 205  
      *
 206  
      * @return The data for this output stream, or {@code null} if no such
 207  
      *         data is available.
 208  
      */
 209  
     public byte[] getData()
 210  
     {
 211  12
         if (memoryOutputStream != null)
 212  
         {
 213  4
             return memoryOutputStream.toByteArray();
 214  
         }
 215  8
         return null;
 216  
     }
 217  
 
 218  
 
 219  
     /**
 220  
      * Returns either the output file specified in the constructor or
 221  
      * the temporary file created or null.
 222  
      * <p>
 223  
      * If the constructor specifying the file is used then it returns that
 224  
      * same output file, even when threshold has not been reached.
 225  
      * <p>
 226  
      * If constructor specifying a temporary file prefix/suffix is used
 227  
      * then the temporary file created once the threshold is reached is returned
 228  
      * If the threshold was not reached then {@code null} is returned.
 229  
      *
 230  
      * @return The file for this output stream, or {@code null} if no such
 231  
      *         file exists.
 232  
      */
 233  
     public File getFile()
 234  
     {
 235  34
         return outputFile;
 236  
     }
 237  
 
 238  
 
 239  
     /**
 240  
      * Closes underlying output stream, and mark this as closed
 241  
      *
 242  
      * @exception IOException if an error occurs.
 243  
      */
 244  
     @Override
 245  
     public void close() throws IOException
 246  
     {
 247  18
         super.close();
 248  18
         closed = true;
 249  18
     }
 250  
 
 251  
 
 252  
     /**
 253  
      * Writes the data from this output stream to the specified output stream,
 254  
      * after it has been closed.
 255  
      *
 256  
      * @param out output stream to write to.
 257  
      * @exception IOException if this stream is not yet closed or an error occurs.
 258  
      */
 259  
     public void writeTo(final OutputStream out) throws IOException
 260  
     {
 261  
         // we may only need to check if this is closed if we are working with a file
 262  
         // but we should force the habit of closing wether we are working with
 263  
         // a file or memory.
 264  8
         if (!closed)
 265  
         {
 266  4
             throw new IOException("Stream not closed");
 267  
         }
 268  
 
 269  4
         if(isInMemory())
 270  
         {
 271  2
             memoryOutputStream.writeTo(out);
 272  
         }
 273  
         else
 274  
         {
 275  2
             final FileInputStream fis = new FileInputStream(outputFile);
 276  
             try {
 277  2
                 IOUtils.copy(fis, out);
 278  
             } finally {
 279  2
                 IOUtils.closeQuietly(fis);
 280  2
             }
 281  
         }
 282  4
     }
 283  
 }