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