1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package io.netty.handler.codec.http.multipart;
17
18 import io.netty.buffer.ByteBuf;
19 import io.netty.handler.codec.http.HttpConstants;
20 import io.netty.util.internal.EmptyArrays;
21 import io.netty.util.internal.logging.InternalLogger;
22 import io.netty.util.internal.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 import static io.netty.buffer.Unpooled.*;
34
35
36
37
38 public abstract class AbstractDiskHttpData extends AbstractHttpData {
39
40 private static final InternalLogger logger = InternalLoggerFactory.getInstance(AbstractDiskHttpData.class);
41
42 protected File file;
43 private boolean isRenamed;
44 private FileChannel fileChannel;
45
46 protected AbstractDiskHttpData(String name, Charset charset, long size) {
47 super(name, charset, size);
48 }
49
50
51
52
53
54 protected abstract String getDiskFilename();
55
56
57
58
59 protected abstract String getPrefix();
60
61
62
63
64 protected abstract String getBaseDirectory();
65
66
67
68
69 protected abstract String getPostfix();
70
71
72
73
74 protected abstract boolean deleteOnExit();
75
76
77
78
79 private File tempFile() throws IOException {
80 String newpostfix;
81 String diskFilename = getDiskFilename();
82 if (diskFilename != null) {
83 newpostfix = '_' + diskFilename;
84 } else {
85 newpostfix = getPostfix();
86 }
87 File tmpFile;
88 if (getBaseDirectory() == null) {
89
90 tmpFile = File.createTempFile(getPrefix(), newpostfix);
91 } else {
92 tmpFile = File.createTempFile(getPrefix(), newpostfix, new File(
93 getBaseDirectory()));
94 }
95 if (deleteOnExit()) {
96 tmpFile.deleteOnExit();
97 }
98 return tmpFile;
99 }
100
101 @Override
102 public void setContent(ByteBuf buffer) throws IOException {
103 if (buffer == null) {
104 throw new NullPointerException("buffer");
105 }
106 try {
107 size = buffer.readableBytes();
108 if (definedSize > 0 && definedSize < size) {
109 throw new IOException("Out of size: " + size + " > " + definedSize);
110 }
111 if (file == null) {
112 file = tempFile();
113 }
114 if (buffer.readableBytes() == 0) {
115
116 file.createNewFile();
117 return;
118 }
119 FileOutputStream outputStream = new FileOutputStream(file);
120 try {
121 FileChannel localfileChannel = outputStream.getChannel();
122 ByteBuffer byteBuffer = buffer.nioBuffer();
123 int written = 0;
124 while (written < size) {
125 written += localfileChannel.write(byteBuffer);
126 }
127 buffer.readerIndex(buffer.readerIndex() + written);
128 localfileChannel.force(false);
129 } finally {
130 outputStream.close();
131 }
132 completed = true;
133 } finally {
134
135
136 buffer.release();
137 }
138 }
139
140 @Override
141 public void addContent(ByteBuf buffer, boolean last)
142 throws IOException {
143 if (buffer != null) {
144 try {
145 int localsize = buffer.readableBytes();
146 if (definedSize > 0 && definedSize < size + localsize) {
147 throw new IOException("Out of size: " + (size + localsize) +
148 " > " + definedSize);
149 }
150 ByteBuffer byteBuffer = buffer.nioBufferCount() == 1 ? buffer.nioBuffer() : buffer.copy().nioBuffer();
151 int written = 0;
152 if (file == null) {
153 file = tempFile();
154 }
155 if (fileChannel == null) {
156 FileOutputStream outputStream = new FileOutputStream(file);
157 fileChannel = outputStream.getChannel();
158 }
159 while (written < localsize) {
160 written += fileChannel.write(byteBuffer);
161 }
162 size += localsize;
163 buffer.readerIndex(buffer.readerIndex() + written);
164 } finally {
165
166
167 buffer.release();
168 }
169 }
170 if (last) {
171 if (file == null) {
172 file = tempFile();
173 }
174 if (fileChannel == null) {
175 FileOutputStream outputStream = new FileOutputStream(file);
176 fileChannel = outputStream.getChannel();
177 }
178 fileChannel.force(false);
179 fileChannel.close();
180 fileChannel = null;
181 completed = true;
182 } else {
183 if (buffer == null) {
184 throw new NullPointerException("buffer");
185 }
186 }
187 }
188
189 @Override
190 public void setContent(File file) throws IOException {
191 if (this.file != null) {
192 delete();
193 }
194 this.file = file;
195 size = file.length();
196 isRenamed = true;
197 completed = true;
198 }
199
200 @Override
201 public void setContent(InputStream inputStream) throws IOException {
202 if (inputStream == null) {
203 throw new NullPointerException("inputStream");
204 }
205 if (file != null) {
206 delete();
207 }
208 file = tempFile();
209 FileOutputStream outputStream = new FileOutputStream(file);
210 int written = 0;
211 try {
212 FileChannel localfileChannel = outputStream.getChannel();
213 byte[] bytes = new byte[4096 * 4];
214 ByteBuffer byteBuffer = ByteBuffer.wrap(bytes);
215 int read = inputStream.read(bytes);
216 while (read > 0) {
217 byteBuffer.position(read).flip();
218 written += localfileChannel.write(byteBuffer);
219 read = inputStream.read(bytes);
220 }
221 localfileChannel.force(false);
222 } finally {
223 outputStream.close();
224 }
225 size = written;
226 if (definedSize > 0 && definedSize < size) {
227 file.delete();
228 file = null;
229 throw new IOException("Out of size: " + size + " > " + definedSize);
230 }
231 isRenamed = true;
232 completed = true;
233 }
234
235 @Override
236 public void delete() {
237 if (fileChannel != null) {
238 try {
239 fileChannel.force(false);
240 fileChannel.close();
241 } catch (IOException e) {
242 logger.warn("Failed to close a file.", e);
243 }
244 fileChannel = null;
245 }
246 if (! isRenamed) {
247 if (file != null && file.exists()) {
248 file.delete();
249 }
250 file = null;
251 }
252 }
253
254 @Override
255 public byte[] get() throws IOException {
256 if (file == null) {
257 return EmptyArrays.EMPTY_BYTES;
258 }
259 return readFrom(file);
260 }
261
262 @Override
263 public ByteBuf getByteBuf() throws IOException {
264 if (file == null) {
265 return EMPTY_BUFFER;
266 }
267 byte[] array = readFrom(file);
268 return wrappedBuffer(array);
269 }
270
271 @Override
272 public ByteBuf getChunk(int length) throws IOException {
273 if (file == null || length == 0) {
274 return EMPTY_BUFFER;
275 }
276 if (fileChannel == null) {
277 FileInputStream inputStream = new FileInputStream(file);
278 fileChannel = inputStream.getChannel();
279 }
280 int read = 0;
281 ByteBuffer byteBuffer = ByteBuffer.allocate(length);
282 while (read < length) {
283 int readnow = fileChannel.read(byteBuffer);
284 if (readnow == -1) {
285 fileChannel.close();
286 fileChannel = null;
287 break;
288 } else {
289 read += readnow;
290 }
291 }
292 if (read == 0) {
293 return EMPTY_BUFFER;
294 }
295 byteBuffer.flip();
296 ByteBuf buffer = wrappedBuffer(byteBuffer);
297 buffer.readerIndex(0);
298 buffer.writerIndex(read);
299 return buffer;
300 }
301
302 @Override
303 public String getString() throws IOException {
304 return getString(HttpConstants.DEFAULT_CHARSET);
305 }
306
307 @Override
308 public String getString(Charset encoding) throws IOException {
309 if (file == null) {
310 return "";
311 }
312 if (encoding == null) {
313 byte[] array = readFrom(file);
314 return new String(array, HttpConstants.DEFAULT_CHARSET.name());
315 }
316 byte[] array = readFrom(file);
317 return new String(array, encoding.name());
318 }
319
320 @Override
321 public boolean isInMemory() {
322 return false;
323 }
324
325 @Override
326 public boolean renameTo(File dest) throws IOException {
327 if (dest == null) {
328 throw new NullPointerException("dest");
329 }
330 if (file == null) {
331 throw new IOException("No file defined so cannot be renamed");
332 }
333 if (!file.renameTo(dest)) {
334
335 IOException exception = null;
336 FileInputStream inputStream = null;
337 FileOutputStream outputStream = null;
338 long chunkSize = 8196;
339 long position = 0;
340 try {
341 inputStream = new FileInputStream(file);
342 outputStream = new FileOutputStream(dest);
343 FileChannel in = inputStream.getChannel();
344 FileChannel out = outputStream.getChannel();
345 while (position < size) {
346 if (chunkSize < size - position) {
347 chunkSize = size - position;
348 }
349 position += in.transferTo(position, chunkSize , out);
350 }
351 } catch (IOException e) {
352 exception = e;
353 } finally {
354 if (inputStream != null) {
355 try {
356 inputStream.close();
357 } catch (IOException e) {
358 if (exception == null) {
359 exception = e;
360 } else {
361 logger.warn("Multiple exceptions detected, the following will be suppressed {}", e);
362 }
363 }
364 }
365 if (outputStream != null) {
366 try {
367 outputStream.close();
368 } catch (IOException e) {
369 if (exception == null) {
370 exception = e;
371 } else {
372 logger.warn("Multiple exceptions detected, the following will be suppressed {}", e);
373 }
374 }
375 }
376 }
377 if (exception != null) {
378 throw exception;
379 }
380 if (position == size) {
381 file.delete();
382 file = dest;
383 isRenamed = true;
384 return true;
385 } else {
386 dest.delete();
387 return false;
388 }
389 }
390 file = dest;
391 isRenamed = true;
392 return true;
393 }
394
395
396
397
398
399 private static byte[] readFrom(File src) throws IOException {
400 long srcsize = src.length();
401 if (srcsize > Integer.MAX_VALUE) {
402 throw new IllegalArgumentException(
403 "File too big to be loaded in memory");
404 }
405 FileInputStream inputStream = new FileInputStream(src);
406 byte[] array = new byte[(int) srcsize];
407 try {
408 FileChannel fileChannel = inputStream.getChannel();
409 ByteBuffer byteBuffer = ByteBuffer.wrap(array);
410 int read = 0;
411 while (read < srcsize) {
412 read += fileChannel.read(byteBuffer);
413 }
414 } finally {
415 inputStream.close();
416 }
417 return array;
418 }
419
420 @Override
421 public File getFile() throws IOException {
422 return file;
423 }
424 }