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 18 19 package org.apache.tomcat.util.http.fileupload; 20 21 import java.io.ByteArrayOutputStream; 22 import java.io.File; 23 import java.io.FileOutputStream; 24 import java.io.IOException; 25 import java.io.OutputStream; 26 27 /** 28 * <p>An output stream which will retain data in memory until a specified 29 * threshold is reached, and only then commit it to disk. If the stream is 30 * closed before the threshold is reached, the data will not be written to 31 * disk at all.</p> 32 * 33 * @author <a href="mailto:martinc@apache.org">Martin Cooper</a> 34 * 35 * @version $Id: DeferredFileOutputStream.java 711597 2008-11-05 16:09:26Z markt $ 36 */ 37 public class DeferredFileOutputStream 38 extends ThresholdingOutputStream 39 { 40 41 // ----------------------------------------------------------- Data members 42 43 44 /** 45 * The output stream to which data will be written prior to the theshold 46 * being reached. 47 */ 48 private ByteArrayOutputStream memoryOutputStream; 49 50 51 /** 52 * The output stream to which data will be written after the theshold is 53 * reached. 54 */ 55 private FileOutputStream diskOutputStream; 56 57 58 /** 59 * The output stream to which data will be written at any given time. This 60 * will always be one of <code>memoryOutputStream</code> or 61 * <code>diskOutputStream</code>. 62 */ 63 private OutputStream currentOutputStream; 64 65 66 /** 67 * The file to which output will be directed if the threshold is exceeded. 68 */ 69 private File outputFile; 70 71 72 // ----------------------------------------------------------- Constructors 73 74 75 /** 76 * Constructs an instance of this class which will trigger an event at the 77 * specified threshold, and save data to a file beyond that point. 78 * 79 * @param threshold The number of bytes at which to trigger an event. 80 * @param outputFile The file to which data is saved beyond the threshold. 81 */ 82 public DeferredFileOutputStream(int threshold, File outputFile) 83 { 84 super(threshold); 85 this.outputFile = outputFile; 86 87 if (threshold < DefaultFileItemFactory.DEFAULT_SIZE_THRESHOLD) { 88 // Small threshold, use it 89 memoryOutputStream = new ByteArrayOutputStream(threshold); 90 } else { 91 // Large threshold. Use default and array will expand if required 92 memoryOutputStream = new ByteArrayOutputStream( 93 DefaultFileItemFactory.DEFAULT_SIZE_THRESHOLD); 94 } 95 currentOutputStream = memoryOutputStream; 96 } 97 98 99 // --------------------------------------- ThresholdingOutputStream methods 100 101 102 /** 103 * Returns the current output stream. This may be memory based or disk 104 * based, depending on the current state with respect to the threshold. 105 * 106 * @return The underlying output stream. 107 * 108 * @exception IOException if an error occurs. 109 */ 110 protected OutputStream getStream() throws IOException 111 { 112 return currentOutputStream; 113 } 114 115 116 /** 117 * Switches the underlying output stream from a memory based stream to one 118 * that is backed by disk. This is the point at which we realise that too 119 * much data is being written to keep in memory, so we elect to switch to 120 * disk-based storage. 121 * 122 * @exception IOException if an error occurs. 123 */ 124 protected void thresholdReached() throws IOException 125 { 126 byte[] data = memoryOutputStream.toByteArray(); 127 FileOutputStream fos = new FileOutputStream(outputFile); 128 fos.write(data); 129 diskOutputStream = fos; 130 currentOutputStream = fos; 131 memoryOutputStream = null; 132 } 133 134 135 // --------------------------------------------------------- Public methods 136 137 138 /** 139 * Determines whether or not the data for this output stream has been 140 * retained in memory. 141 * 142 * @return <code>true</code> if the data is available in memory; 143 * <code>false</code> otherwise. 144 */ 145 public boolean isInMemory() 146 { 147 return (!isThresholdExceeded()); 148 } 149 150 151 /** 152 * Returns the data for this output stream as an array of bytes, assuming 153 * that the data has been retained in memory. If the data was written to 154 * disk, this method returns <code>null</code>. 155 * 156 * @return The data for this output stream, or <code>null</code> if no such 157 * data is available. 158 */ 159 public byte[] getData() 160 { 161 if (memoryOutputStream != null) 162 { 163 return memoryOutputStream.toByteArray(); 164 } 165 return null; 166 } 167 168 169 /** 170 * Returns the data for this output stream as a <code>File</code>, assuming 171 * that the data was written to disk. If the data was retained in memory, 172 * this method returns <code>null</code>. 173 * 174 * @return The file for this output stream, or <code>null</code> if no such 175 * file exists. 176 */ 177 public File getFile() 178 { 179 return outputFile; 180 } 181 }