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 java.io.File;
19  import java.io.FileInputStream;
20  import java.io.FileOutputStream;
21  import java.io.IOException;
22  import java.io.InputStream;
23  import java.nio.ByteBuffer;
24  import java.nio.channels.FileChannel;
25  import java.nio.charset.Charset;
26  
27  import org.jboss.netty.buffer.ChannelBuffer;
28  import org.jboss.netty.buffer.ChannelBuffers;
29  import org.jboss.netty.handler.codec.http.HttpConstants;
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      public 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       *
72       * @return a new Temp File from getDiskFilename(), default prefix, postfix and baseDirectory
73       * @throws IOException
74       */
75      private File tempFile() throws IOException {
76          String newpostfix = null;
77          String diskFilename = getDiskFilename();
78          if (diskFilename != null) {
79              newpostfix = "_" + diskFilename;
80          } else {
81              newpostfix = getPostfix();
82          }
83          File tmpFile;
84          if (getBaseDirectory() == null) {
85              // create a temporary file
86              tmpFile = File.createTempFile(getPrefix(), newpostfix);
87          } else {
88              tmpFile = File.createTempFile(getPrefix(), newpostfix, new File(
89                      getBaseDirectory()));
90          }
91          if (deleteOnExit()) {
92              tmpFile.deleteOnExit();
93          }
94          return tmpFile;
95      }
96  
97      public void setContent(ChannelBuffer buffer) throws IOException {
98          if (buffer == null) {
99              throw new NullPointerException("buffer");
100         }
101         size = buffer.readableBytes();
102         if (definedSize > 0 && definedSize < size) {
103             throw new IOException("Out of size: " + size + " > " + definedSize);
104         }
105         if (file == null) {
106             file = tempFile();
107         }
108         if (buffer.readableBytes() == 0) {
109             // empty file
110             file.createNewFile();
111             return;
112         }
113         FileOutputStream outputStream = new FileOutputStream(file);
114         FileChannel localfileChannel = outputStream.getChannel();
115         ByteBuffer byteBuffer = buffer.toByteBuffer();
116         int written = 0;
117         while (written < size) {
118             written += localfileChannel.write(byteBuffer);
119         }
120         buffer.readerIndex(buffer.readerIndex() + written);
121         localfileChannel.force(false);
122         localfileChannel.close();
123         completed = true;
124     }
125 
126     public void addContent(ChannelBuffer buffer, boolean last)
127             throws IOException {
128         if (buffer != null) {
129             int localsize = buffer.readableBytes();
130             if (definedSize > 0 && definedSize < size + localsize) {
131                 throw new IOException("Out of size: " + (size + localsize) +
132                         " > " + definedSize);
133             }
134             ByteBuffer byteBuffer = buffer.toByteBuffer();
135             int written = 0;
136             if (file == null) {
137                 file = tempFile();
138             }
139             if (fileChannel == null) {
140                 FileOutputStream outputStream = new FileOutputStream(file);
141                 fileChannel = outputStream.getChannel();
142             }
143             while (written < localsize) {
144                 written += fileChannel.write(byteBuffer);
145             }
146             size += localsize;
147             buffer.readerIndex(buffer.readerIndex() + written);
148         }
149         if (last) {
150             if (file == null) {
151                 file = tempFile();
152             }
153             if (fileChannel == null) {
154                 FileOutputStream outputStream = new FileOutputStream(file);
155                 fileChannel = outputStream.getChannel();
156             }
157             fileChannel.force(false);
158             fileChannel.close();
159             fileChannel = null;
160             completed = true;
161         } else {
162             if (buffer == null) {
163                 throw new NullPointerException("buffer");
164             }
165         }
166     }
167 
168     public void setContent(File file) throws IOException {
169         if (this.file != null) {
170             delete();
171         }
172         this.file = file;
173         size = file.length();
174         isRenamed = true;
175         completed = true;
176     }
177 
178     public void setContent(InputStream inputStream) throws IOException {
179         if (inputStream == null) {
180             throw new NullPointerException("inputStream");
181         }
182         if (file != null) {
183             delete();
184         }
185         file = tempFile();
186         FileOutputStream outputStream = new FileOutputStream(file);
187         FileChannel localfileChannel = outputStream.getChannel();
188         byte[] bytes = new byte[4096 * 4];
189         ByteBuffer byteBuffer = ByteBuffer.wrap(bytes);
190         int read = inputStream.read(bytes);
191         int written = 0;
192         while (read > 0) {
193             byteBuffer.position(read).flip();
194             written += localfileChannel.write(byteBuffer);
195             read = inputStream.read(bytes);
196         }
197         localfileChannel.force(false);
198         localfileChannel.close();
199         size = written;
200         if (definedSize > 0 && definedSize < size) {
201             file.delete();
202             file = null;
203             throw new IOException("Out of size: " + size + " > " + definedSize);
204         }
205         isRenamed = true;
206         completed = true;
207     }
208 
209     public void delete() {
210         if (! isRenamed) {
211             if (file != null) {
212                 file.delete();
213             }
214         }
215     }
216 
217     public byte[] get() throws IOException {
218         if (file == null) {
219             return new byte[0];
220         }
221         return readFrom(file);
222     }
223 
224     public ChannelBuffer getChannelBuffer() throws IOException {
225         if (file == null) {
226             return ChannelBuffers.EMPTY_BUFFER;
227         }
228         byte[] array = readFrom(file);
229         return ChannelBuffers.wrappedBuffer(array);
230     }
231 
232     public ChannelBuffer getChunk(int length) throws IOException {
233         if (file == null || length == 0) {
234             return ChannelBuffers.EMPTY_BUFFER;
235         }
236         if (fileChannel == null) {
237             FileInputStream  inputStream = new FileInputStream(file);
238             fileChannel = inputStream.getChannel();
239         }
240         int read = 0;
241         ByteBuffer byteBuffer = ByteBuffer.allocate(length);
242         while (read < length) {
243             int readnow = fileChannel.read(byteBuffer);
244             if (readnow == -1) {
245                 fileChannel.close();
246                 fileChannel = null;
247                 break;
248             } else {
249                 read += readnow;
250             }
251         }
252         if (read == 0) {
253             return ChannelBuffers.EMPTY_BUFFER;
254         }
255         byteBuffer.flip();
256         ChannelBuffer buffer = ChannelBuffers.wrappedBuffer(byteBuffer);
257         buffer.readerIndex(0);
258         buffer.writerIndex(read);
259         return buffer;
260     }
261 
262     public String getString() throws IOException {
263         return getString(HttpConstants.DEFAULT_CHARSET);
264     }
265 
266     public String getString(Charset encoding) throws IOException {
267         if (file == null) {
268             return "";
269         }
270         if (encoding == null) {
271             byte[] array = readFrom(file);
272             return new String(array, HttpConstants.DEFAULT_CHARSET.name());
273         }
274         byte[] array = readFrom(file);
275         return new String(array, encoding.name());
276     }
277 
278     public boolean isInMemory() {
279         return false;
280     }
281 
282     public boolean renameTo(File dest) throws IOException {
283         if (dest == null) {
284             throw new NullPointerException("dest");
285         }
286         if (!file.renameTo(dest)) {
287             // must copy
288             FileInputStream inputStream = new FileInputStream(file);
289             FileOutputStream outputStream = new FileOutputStream(dest);
290             FileChannel in = inputStream.getChannel();
291             FileChannel out = outputStream.getChannel();
292             int chunkSize = 8196;
293             long position = 0;
294             while (position < size) {
295                 if (chunkSize < size - position) {
296                     chunkSize = (int) (size - position);
297                 }
298                 position += in.transferTo(position, chunkSize , out);
299             }
300             in.close();
301             out.close();
302             if (position == size) {
303                 file.delete();
304                 file = dest;
305                 isRenamed = true;
306                 return true;
307             } else {
308                 dest.delete();
309                 return false;
310             }
311         }
312         file = dest;
313         isRenamed = true;
314         return true;
315     }
316 
317     /**
318      * Utility function
319      * @param src
320      * @return the array of bytes
321      * @throws IOException
322      */
323     private static byte[] readFrom(File src) throws IOException {
324         long srcsize = src.length();
325         if (srcsize > Integer.MAX_VALUE) {
326             throw new IllegalArgumentException(
327                     "File too big to be loaded in memory");
328         }
329         FileInputStream inputStream = new FileInputStream(src);
330         FileChannel fileChannel = inputStream.getChannel();
331         byte[] array = new byte[(int) srcsize];
332         ByteBuffer byteBuffer = ByteBuffer.wrap(array);
333         int read = 0;
334         while (read < srcsize) {
335             read += fileChannel.read(byteBuffer);
336         }
337         fileChannel.close();
338         return array;
339     }
340 
341     public File getFile() throws IOException {
342         return file;
343     }
344 
345 }