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