View Javadoc

1   /*
2    * Copyright 2012 The Netty Project
3    *
4    * The Netty Project licenses this file to you under the Apache License,
5    * version 2.0 (the "License"); you may not use this file except in compliance
6    * with the License. You may obtain a copy of the License at:
7    *
8    *   http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12   * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13   * License for the specific language governing permissions and limitations
14   * under the License.
15   */
16  package org.jboss.netty.handler.codec.http.multipart;
17  
18  import org.jboss.netty.buffer.ChannelBuffer;
19  import org.jboss.netty.buffer.ChannelBuffers;
20  import org.jboss.netty.handler.codec.http.HttpConstants;
21  
22  import java.io.File;
23  import java.io.FileInputStream;
24  import java.io.FileOutputStream;
25  import java.io.IOException;
26  import java.io.InputStream;
27  import java.nio.ByteBuffer;
28  import java.nio.channels.FileChannel;
29  import java.nio.charset.Charset;
30  
31  /**
32   * Abstract Disk HttpData implementation
33   */
34  public abstract class AbstractDiskHttpData extends AbstractHttpData {
35  
36      protected File file;
37      private boolean isRenamed;
38      private FileChannel fileChannel;
39  
40      protected AbstractDiskHttpData(String name, Charset charset, long size) {
41          super(name, charset, size);
42      }
43  
44      /**
45       *
46       * @return the real DiskFilename (basename)
47       */
48      protected abstract String getDiskFilename();
49      /**
50       *
51       * @return the default prefix
52       */
53      protected abstract String getPrefix();
54      /**
55       *
56       * @return the default base Directory
57       */
58      protected abstract String getBaseDirectory();
59      /**
60       *
61       * @return the default postfix
62       */
63      protected abstract String getPostfix();
64      /**
65       *
66       * @return True if the file should be deleted on Exit by default
67       */
68      protected abstract boolean deleteOnExit();
69  
70      /**
71       * @return a new Temp File from getDiskFilename(), default prefix, postfix and baseDirectory
72       */
73      private File tempFile() throws IOException {
74          String newpostfix;
75          String diskFilename = getDiskFilename();
76          if (diskFilename != null) {
77              newpostfix = '_' + diskFilename;
78          } else {
79              newpostfix = getPostfix();
80          }
81          File tmpFile;
82          if (getBaseDirectory() == null) {
83              // create a temporary file
84              tmpFile = File.createTempFile(getPrefix(), newpostfix);
85          } else {
86              tmpFile = File.createTempFile(getPrefix(), newpostfix, new File(
87                      getBaseDirectory()));
88          }
89          if (deleteOnExit()) {
90              tmpFile.deleteOnExit();
91          }
92          return tmpFile;
93      }
94  
95      public void setContent(ChannelBuffer buffer) throws IOException {
96          if (buffer == null) {
97              throw new NullPointerException("buffer");
98          }
99          size = buffer.readableBytes();
100         if (definedSize > 0 && definedSize < size) {
101             throw new IOException("Out of size: " + size + " > " + definedSize);
102         }
103         if (file == null) {
104             file = tempFile();
105         }
106         if (buffer.readableBytes() == 0) {
107             // empty file
108             file.createNewFile();
109             return;
110         }
111         FileOutputStream outputStream = new FileOutputStream(file);
112         FileChannel localfileChannel = outputStream.getChannel();
113         ByteBuffer byteBuffer = buffer.toByteBuffer();
114         int written = 0;
115         while (written < size) {
116             written += localfileChannel.write(byteBuffer);
117         }
118         buffer.readerIndex(buffer.readerIndex() + written);
119         localfileChannel.force(false);
120         localfileChannel.close();
121         outputStream.close();
122         completed = true;
123     }
124 
125     public void addContent(ChannelBuffer buffer, boolean last)
126             throws IOException {
127         if (buffer != null) {
128             int localsize = buffer.readableBytes();
129             if (definedSize > 0 && definedSize < size + localsize) {
130                 throw new IOException("Out of size: " + (size + localsize) +
131                         " > " + definedSize);
132             }
133             ByteBuffer byteBuffer = buffer.toByteBuffer();
134             int written = 0;
135             if (file == null) {
136                 file = tempFile();
137             }
138             if (fileChannel == null) {
139                 FileOutputStream outputStream = new FileOutputStream(file);
140                 fileChannel = outputStream.getChannel();
141             }
142             while (written < localsize) {
143                 written += fileChannel.write(byteBuffer);
144             }
145             size += localsize;
146             buffer.readerIndex(buffer.readerIndex() + written);
147         }
148         if (last) {
149             if (file == null) {
150                 file = tempFile();
151             }
152             if (fileChannel == null) {
153                 FileOutputStream outputStream = new FileOutputStream(file);
154                 fileChannel = outputStream.getChannel();
155             }
156             fileChannel.force(false);
157             fileChannel.close();
158             fileChannel = null;
159             completed = true;
160         } else {
161             if (buffer == null) {
162                 throw new NullPointerException("buffer");
163             }
164         }
165     }
166 
167     public void setContent(File file) throws IOException {
168         if (this.file != null) {
169             delete();
170         }
171         this.file = file;
172         size = file.length();
173         isRenamed = true;
174         completed = true;
175     }
176 
177     public void setContent(InputStream inputStream) throws IOException {
178         if (inputStream == null) {
179             throw new NullPointerException("inputStream");
180         }
181         if (file != null) {
182             delete();
183         }
184         file = tempFile();
185         FileOutputStream outputStream = new FileOutputStream(file);
186         FileChannel localfileChannel = outputStream.getChannel();
187         byte[] bytes = new byte[4096 * 4];
188         ByteBuffer byteBuffer = ByteBuffer.wrap(bytes);
189         int read = inputStream.read(bytes);
190         int written = 0;
191         while (read > 0) {
192             byteBuffer.position(read).flip();
193             written += localfileChannel.write(byteBuffer);
194             read = inputStream.read(bytes);
195         }
196         localfileChannel.force(false);
197         localfileChannel.close();
198         size = written;
199         if (definedSize > 0 && definedSize < size) {
200             file.delete();
201             file = null;
202             throw new IOException("Out of size: " + size + " > " + definedSize);
203         }
204         isRenamed = true;
205         completed = true;
206     }
207 
208     public void delete() {
209         if (! isRenamed) {
210             if (file != null) {
211                 file.delete();
212             }
213         }
214     }
215 
216     public byte[] get() throws IOException {
217         if (file == null) {
218             return new byte[0];
219         }
220         return readFrom(file);
221     }
222 
223     public ChannelBuffer getChannelBuffer() throws IOException {
224         if (file == null) {
225             return ChannelBuffers.EMPTY_BUFFER;
226         }
227         byte[] array = readFrom(file);
228         return ChannelBuffers.wrappedBuffer(array);
229     }
230 
231     public ChannelBuffer getChunk(int length) throws IOException {
232         if (file == null || length == 0) {
233             return ChannelBuffers.EMPTY_BUFFER;
234         }
235         if (fileChannel == null) {
236             FileInputStream  inputStream = new FileInputStream(file);
237             fileChannel = inputStream.getChannel();
238         }
239         int read = 0;
240         ByteBuffer byteBuffer = ByteBuffer.allocate(length);
241         while (read < length) {
242             int readnow = fileChannel.read(byteBuffer);
243             if (readnow == -1) {
244                 fileChannel.close();
245                 fileChannel = null;
246                 break;
247             } else {
248                 read += readnow;
249             }
250         }
251         if (read == 0) {
252             return ChannelBuffers.EMPTY_BUFFER;
253         }
254         byteBuffer.flip();
255         ChannelBuffer buffer = ChannelBuffers.wrappedBuffer(byteBuffer);
256         buffer.readerIndex(0);
257         buffer.writerIndex(read);
258         return buffer;
259     }
260 
261     public String getString() throws IOException {
262         return getString(HttpConstants.DEFAULT_CHARSET);
263     }
264 
265     public String getString(Charset encoding) throws IOException {
266         if (file == null) {
267             return "";
268         }
269         if (encoding == null) {
270             byte[] array = readFrom(file);
271             return new String(array, HttpConstants.DEFAULT_CHARSET.name());
272         }
273         byte[] array = readFrom(file);
274         return new String(array, encoding.name());
275     }
276 
277     public boolean isInMemory() {
278         return false;
279     }
280 
281     public boolean renameTo(File dest) throws IOException {
282         if (dest == null) {
283             throw new NullPointerException("dest");
284         }
285         if (!file.renameTo(dest)) {
286             // must copy
287             FileInputStream inputStream = new FileInputStream(file);
288             FileOutputStream outputStream = new FileOutputStream(dest);
289             FileChannel in = inputStream.getChannel();
290             FileChannel out = outputStream.getChannel();
291             int chunkSize = 8196;
292             long position = 0;
293             while (position < size) {
294                 if (chunkSize < size - position) {
295                     chunkSize = (int) (size - position);
296                 }
297                 position += in.transferTo(position, chunkSize , out);
298             }
299             in.close();
300             out.close();
301             if (position == size) {
302                 file.delete();
303                 file = dest;
304                 isRenamed = true;
305                 return true;
306             } else {
307                 dest.delete();
308                 return false;
309             }
310         }
311         file = dest;
312         isRenamed = true;
313         return true;
314     }
315 
316     /**
317      * Utility function
318      * @return the array of bytes
319      */
320     private static byte[] readFrom(File src) throws IOException {
321         long srcsize = src.length();
322         if (srcsize > Integer.MAX_VALUE) {
323             throw new IllegalArgumentException(
324                     "File too big to be loaded in memory");
325         }
326         FileInputStream inputStream = new FileInputStream(src);
327         FileChannel fileChannel = inputStream.getChannel();
328         byte[] array = new byte[(int) srcsize];
329         ByteBuffer byteBuffer = ByteBuffer.wrap(array);
330         int read = 0;
331         while (read < srcsize) {
332             read += fileChannel.read(byteBuffer);
333         }
334         fileChannel.close();
335         return array;
336     }
337 
338     public File getFile() throws IOException {
339         return file;
340     }
341 }