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