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 io.netty.handler.codec.http.multipart;
17  
18  import io.netty.buffer.ByteBuf;
19  import io.netty.buffer.CompositeByteBuf;
20  import io.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  import static io.netty.buffer.Unpooled.*;
32  
33  /**
34   * Abstract Memory HttpData implementation
35   */
36  public abstract class AbstractMemoryHttpData extends AbstractHttpData {
37  
38      private ByteBuf byteBuf;
39      private int chunkPosition;
40  
41      protected AbstractMemoryHttpData(String name, Charset charset, long size) {
42          super(name, charset, size);
43      }
44  
45      @Override
46      public void setContent(ByteBuf buffer) throws IOException {
47          if (buffer == null) {
48              throw new NullPointerException("buffer");
49          }
50          long localsize = buffer.readableBytes();
51          checkSize(localsize);
52          if (definedSize > 0 && definedSize < localsize) {
53              throw new IOException("Out of size: " + localsize + " > " +
54                      definedSize);
55          }
56          if (byteBuf != null) {
57              byteBuf.release();
58          }
59          byteBuf = buffer;
60          size = localsize;
61          setCompleted();
62      }
63  
64      @Override
65      public void setContent(InputStream inputStream) throws IOException {
66          if (inputStream == null) {
67              throw new NullPointerException("inputStream");
68          }
69          ByteBuf buffer = buffer();
70          byte[] bytes = new byte[4096 * 4];
71          int read = inputStream.read(bytes);
72          int written = 0;
73          while (read > 0) {
74              buffer.writeBytes(bytes, 0, read);
75              written += read;
76              checkSize(written);
77              read = inputStream.read(bytes);
78          }
79          size = written;
80          if (definedSize > 0 && definedSize < size) {
81              throw new IOException("Out of size: " + size + " > " + definedSize);
82          }
83          if (byteBuf != null) {
84              byteBuf.release();
85          }
86          byteBuf = buffer;
87          setCompleted();
88      }
89  
90      @Override
91      public void addContent(ByteBuf buffer, boolean last)
92              throws IOException {
93          if (buffer != null) {
94              long localsize = buffer.readableBytes();
95              checkSize(size + localsize);
96              if (definedSize > 0 && definedSize < size + localsize) {
97                  throw new IOException("Out of size: " + (size + localsize) +
98                          " > " + definedSize);
99              }
100             size += localsize;
101             if (byteBuf == null) {
102                 byteBuf = buffer;
103             } else if (byteBuf instanceof CompositeByteBuf) {
104                 CompositeByteBuf cbb = (CompositeByteBuf) byteBuf;
105                 cbb.addComponent(buffer);
106                 cbb.writerIndex(cbb.writerIndex() + buffer.readableBytes());
107             } else {
108                 CompositeByteBuf cbb = compositeBuffer(Integer.MAX_VALUE);
109                 cbb.addComponents(byteBuf, buffer);
110                 cbb.writerIndex(byteBuf.readableBytes() + buffer.readableBytes());
111                 byteBuf = cbb;
112             }
113         }
114         if (last) {
115             setCompleted();
116         } else {
117             if (buffer == null) {
118                 throw new NullPointerException("buffer");
119             }
120         }
121     }
122 
123     @Override
124     public void setContent(File file) throws IOException {
125         if (file == null) {
126             throw new NullPointerException("file");
127         }
128         long newsize = file.length();
129         if (newsize > Integer.MAX_VALUE) {
130             throw new IllegalArgumentException(
131                     "File too big to be loaded in memory");
132         }
133         checkSize(newsize);
134         FileInputStream inputStream = new FileInputStream(file);
135         FileChannel fileChannel = inputStream.getChannel();
136         byte[] array = new byte[(int) newsize];
137         ByteBuffer byteBuffer = ByteBuffer.wrap(array);
138         int read = 0;
139         while (read < newsize) {
140             read += fileChannel.read(byteBuffer);
141         }
142         fileChannel.close();
143         inputStream.close();
144         byteBuffer.flip();
145         if (byteBuf != null) {
146             byteBuf.release();
147         }
148         byteBuf = wrappedBuffer(Integer.MAX_VALUE, byteBuffer);
149         size = newsize;
150         setCompleted();
151     }
152 
153     @Override
154     public void delete() {
155         if (byteBuf != null) {
156             byteBuf.release();
157             byteBuf = null;
158         }
159     }
160 
161     @Override
162     public byte[] get() {
163         if (byteBuf == null) {
164             return EMPTY_BUFFER.array();
165         }
166         byte[] array = new byte[byteBuf.readableBytes()];
167         byteBuf.getBytes(byteBuf.readerIndex(), array);
168         return array;
169     }
170 
171     @Override
172     public String getString() {
173         return getString(HttpConstants.DEFAULT_CHARSET);
174     }
175 
176     @Override
177     public String getString(Charset encoding) {
178         if (byteBuf == null) {
179             return "";
180         }
181         if (encoding == null) {
182             encoding = HttpConstants.DEFAULT_CHARSET;
183         }
184         return byteBuf.toString(encoding);
185     }
186 
187     /**
188      * Utility to go from a In Memory FileUpload
189      * to a Disk (or another implementation) FileUpload
190      * @return the attached ByteBuf containing the actual bytes
191      */
192     @Override
193     public ByteBuf getByteBuf() {
194         return byteBuf;
195     }
196 
197     @Override
198     public ByteBuf getChunk(int length) throws IOException {
199         if (byteBuf == null || length == 0 || byteBuf.readableBytes() == 0) {
200             chunkPosition = 0;
201             return EMPTY_BUFFER;
202         }
203         int sizeLeft = byteBuf.readableBytes() - chunkPosition;
204         if (sizeLeft == 0) {
205             chunkPosition = 0;
206             return EMPTY_BUFFER;
207         }
208         int sliceLength = length;
209         if (sizeLeft < length) {
210             sliceLength = sizeLeft;
211         }
212         ByteBuf chunk = byteBuf.slice(chunkPosition, sliceLength).retain();
213         chunkPosition += sliceLength;
214         return chunk;
215     }
216 
217     @Override
218     public boolean isInMemory() {
219         return true;
220     }
221 
222     @Override
223     public boolean renameTo(File dest) throws IOException {
224         if (dest == null) {
225             throw new NullPointerException("dest");
226         }
227         if (byteBuf == null) {
228             // empty file
229             if (!dest.createNewFile()) {
230                 throw new IOException("file exists already: " + dest);
231             }
232             return true;
233         }
234         int length = byteBuf.readableBytes();
235         FileOutputStream outputStream = new FileOutputStream(dest);
236         FileChannel fileChannel = outputStream.getChannel();
237         int written = 0;
238         if (byteBuf.nioBufferCount() == 1) {
239             ByteBuffer byteBuffer = byteBuf.nioBuffer();
240             while (written < length) {
241                 written += fileChannel.write(byteBuffer);
242             }
243         } else {
244             ByteBuffer[] byteBuffers = byteBuf.nioBuffers();
245             while (written < length) {
246                 written += fileChannel.write(byteBuffers);
247             }
248         }
249 
250         fileChannel.force(false);
251         fileChannel.close();
252         outputStream.close();
253         return written == length;
254     }
255 
256     @Override
257     public File getFile() throws IOException {
258         throw new IOException("Not represented by a file");
259     }
260 
261     @Override
262     public HttpData touch() {
263         return touch(null);
264     }
265 
266     @Override
267     public HttpData touch(Object hint) {
268         if (byteBuf != null) {
269             byteBuf.touch(hint);
270         }
271         return this;
272     }
273 }