001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017: package org.apache.commons.io.output;
018:
019: import java.io.File;
020: import java.io.FileInputStream;
021: import java.io.FileOutputStream;
022: import java.io.IOException;
023: import java.io.OutputStream;
024: import org.apache.commons.io.IOUtils;
025:
026: /**
027: * An output stream which will retain data in memory until a specified
028: * threshold is reached, and only then commit it to disk. If the stream is
029: * closed before the threshold is reached, the data will not be written to
030: * disk at all.
031: * <p>
032: * This class originated in FileUpload processing. In this use case, you do
033: * not know in advance the size of the file being uploaded. If the file is small
034: * you want to store it in memory (for speed), but if the file is large you want
035: * to store it to file (to avoid memory issues).
036: *
037: * @author <a href="mailto:martinc@apache.org">Martin Cooper</a>
038: * @author gaxzerow
039: *
040: * @version $Id: DeferredFileOutputStream.java 437567 2006-08-28 06:39:07Z bayard $
041: */
042: public class DeferredFileOutputStream extends ThresholdingOutputStream {
043:
044: // ----------------------------------------------------------- Data members
045:
046: /**
047: * The output stream to which data will be written prior to the theshold
048: * being reached.
049: */
050: private ByteArrayOutputStream memoryOutputStream;
051:
052: /**
053: * The output stream to which data will be written at any given time. This
054: * will always be one of <code>memoryOutputStream</code> or
055: * <code>diskOutputStream</code>.
056: */
057: private OutputStream currentOutputStream;
058:
059: /**
060: * The file to which output will be directed if the threshold is exceeded.
061: */
062: private File outputFile;
063:
064: /**
065: * True when close() has been called successfully.
066: */
067: private boolean closed = false;
068:
069: // ----------------------------------------------------------- Constructors
070:
071: /**
072: * Constructs an instance of this class which will trigger an event at the
073: * specified threshold, and save data to a file beyond that point.
074: *
075: * @param threshold The number of bytes at which to trigger an event.
076: * @param outputFile The file to which data is saved beyond the threshold.
077: */
078: public DeferredFileOutputStream(int threshold, File outputFile) {
079: super (threshold);
080: this .outputFile = outputFile;
081:
082: memoryOutputStream = new ByteArrayOutputStream();
083: currentOutputStream = memoryOutputStream;
084: }
085:
086: // --------------------------------------- ThresholdingOutputStream methods
087:
088: /**
089: * Returns the current output stream. This may be memory based or disk
090: * based, depending on the current state with respect to the threshold.
091: *
092: * @return The underlying output stream.
093: *
094: * @exception IOException if an error occurs.
095: */
096: protected OutputStream getStream() throws IOException {
097: return currentOutputStream;
098: }
099:
100: /**
101: * Switches the underlying output stream from a memory based stream to one
102: * that is backed by disk. This is the point at which we realise that too
103: * much data is being written to keep in memory, so we elect to switch to
104: * disk-based storage.
105: *
106: * @exception IOException if an error occurs.
107: */
108: protected void thresholdReached() throws IOException {
109: FileOutputStream fos = new FileOutputStream(outputFile);
110: memoryOutputStream.writeTo(fos);
111: currentOutputStream = fos;
112: memoryOutputStream = null;
113: }
114:
115: // --------------------------------------------------------- Public methods
116:
117: /**
118: * Determines whether or not the data for this output stream has been
119: * retained in memory.
120: *
121: * @return <code>true</code> if the data is available in memory;
122: * <code>false</code> otherwise.
123: */
124: public boolean isInMemory() {
125: return (!isThresholdExceeded());
126: }
127:
128: /**
129: * Returns the data for this output stream as an array of bytes, assuming
130: * that the data has been retained in memory. If the data was written to
131: * disk, this method returns <code>null</code>.
132: *
133: * @return The data for this output stream, or <code>null</code> if no such
134: * data is available.
135: */
136: public byte[] getData() {
137: if (memoryOutputStream != null) {
138: return memoryOutputStream.toByteArray();
139: }
140: return null;
141: }
142:
143: /**
144: * Returns the same output file specified in the constructor, even when
145: * threashold has not been reached.
146: *
147: * @return The file for this output stream, or <code>null</code> if no such
148: * file exists.
149: */
150: public File getFile() {
151: return outputFile;
152: }
153:
154: /**
155: * Closes underlying output stream, and mark this as closed
156: *
157: * @exception IOException if an error occurs.
158: */
159: public void close() throws IOException {
160: super .close();
161: closed = true;
162: }
163:
164: /**
165: * Writes the data from this output stream to the specified output stream,
166: * after it has been closed.
167: *
168: * @param out output stream to write to.
169: * @exception IOException if this stream is not yet closed or an error occurs.
170: */
171: public void writeTo(OutputStream out) throws IOException {
172: // we may only need to check if this is closed if we are working with a file
173: // but we should force the habit of closing wether we are working with
174: // a file or memory.
175: if (!closed) {
176: throw new IOException("Stream not closed");
177: }
178:
179: if (isInMemory()) {
180: memoryOutputStream.writeTo(out);
181: } else {
182: FileInputStream fis = new FileInputStream(outputFile);
183: try {
184: IOUtils.copy(fis, out);
185: } finally {
186: IOUtils.closeQuietly(fis);
187: }
188: }
189: }
190: }
|