KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > net > myvietnam > mvncore > web > fileupload > disk > DiskFileItem


1 /*
2  * Copyright 2001-2005 The Apache Software Foundation
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * 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,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */

16 package net.myvietnam.mvncore.web.fileupload.disk;
17
18 import java.io.BufferedInputStream JavaDoc;
19 import java.io.BufferedOutputStream JavaDoc;
20 import java.io.ByteArrayInputStream JavaDoc;
21 import java.io.File JavaDoc;
22 import java.io.FileInputStream JavaDoc;
23 import java.io.FileOutputStream JavaDoc;
24 import java.io.IOException JavaDoc;
25 import java.io.InputStream JavaDoc;
26 import java.io.OutputStream JavaDoc;
27 import java.io.ObjectOutputStream JavaDoc;
28 import java.io.ObjectInputStream JavaDoc;
29 import java.io.UnsupportedEncodingException JavaDoc;
30 import java.rmi.server.UID JavaDoc;
31 import java.util.Map JavaDoc;
32 import org.apache.commons.io.IOUtils;
33 import org.apache.commons.io.FileCleaner;
34 import org.apache.commons.io.output.DeferredFileOutputStream;
35
36 import net.myvietnam.mvncore.web.fileupload.FileItem;
37 import net.myvietnam.mvncore.web.fileupload.FileUploadException;
38 import net.myvietnam.mvncore.web.fileupload.ParameterParser;
39
40
41 /**
42  * <p> The default implementation of the
43  * {@link org.apache.commons.fileupload.FileItem FileItem} interface.
44  *
45  * <p> After retrieving an instance of this class from a {@link
46  * org.apache.commons.fileupload.DiskFileUpload DiskFileUpload} instance (see
47  * {@link org.apache.commons.fileupload.DiskFileUpload
48  * #parseRequest(javax.servlet.http.HttpServletRequest)}), you may
49  * either request all contents of file at once using {@link #get()} or
50  * request an {@link java.io.InputStream InputStream} with
51  * {@link #getInputStream()} and process the file without attempting to load
52  * it into memory, which may come handy with large files.
53  *
54  * @author <a HREF="mailto:Rafal.Krzewski@e-point.pl">Rafal Krzewski</a>
55  * @author <a HREF="mailto:sean@informage.net">Sean Legassick</a>
56  * @author <a HREF="mailto:jvanzyl@apache.org">Jason van Zyl</a>
57  * @author <a HREF="mailto:jmcnally@apache.org">John McNally</a>
58  * @author <a HREF="mailto:martinc@apache.org">Martin Cooper</a>
59  * @author Sean C. Sullivan
60  *
61  * @since FileUpload 1.1
62  *
63  * @version $Id: DiskFileItem.java,v 1.2 2006/02/12 04:43:11 minhnn Exp $
64  */

65 public class DiskFileItem
66     implements FileItem {
67
68     // ----------------------------------------------------- Manifest constants
69

70
71     /**
72      * Default content charset to be used when no explicit charset
73      * parameter is provided by the sender. Media subtypes of the
74      * "text" type are defined to have a default charset value of
75      * "ISO-8859-1" when received via HTTP.
76      */

77     public static final String JavaDoc DEFAULT_CHARSET = "ISO-8859-1";
78
79
80     // ----------------------------------------------------------- Data members
81

82
83     /**
84      * UID used in unique file name generation.
85      */

86     private static final String JavaDoc UID =
87             new UID JavaDoc().toString().replace(':', '_').replace('-', '_');
88
89     /**
90      * Counter used in unique identifier generation.
91      */

92     private static int counter = 0;
93
94
95     /**
96      * The name of the form field as provided by the browser.
97      */

98     private String JavaDoc fieldName;
99
100
101     /**
102      * The content type passed by the browser, or <code>null</code> if
103      * not defined.
104      */

105     private String JavaDoc contentType;
106
107
108     /**
109      * Whether or not this item is a simple form field.
110      */

111     private boolean isFormField;
112
113
114     /**
115      * The original filename in the user's filesystem.
116      */

117     private String JavaDoc fileName;
118
119
120     /**
121      * The threshold above which uploads will be stored on disk.
122      */

123     private int sizeThreshold;
124
125
126     /**
127      * The directory in which uploaded files will be stored, if stored on disk.
128      */

129     private File JavaDoc repository;
130
131
132     /**
133      * Cached contents of the file.
134      */

135     private byte[] cachedContent;
136
137
138     /**
139      * Output stream for this item.
140      */

141     private transient DeferredFileOutputStream dfos;
142
143     /**
144      * File to allow for serialization of the content of this item.
145      */

146     private File JavaDoc dfosFile;
147
148
149     // ----------------------------------------------------------- Constructors
150

151
152     /**
153      * Constructs a new <code>DiskFileItem</code> instance.
154      *
155      * @param fieldName The name of the form field.
156      * @param contentType The content type passed by the browser or
157      * <code>null</code> if not specified.
158      * @param isFormField Whether or not this item is a plain form field, as
159      * opposed to a file upload.
160      * @param fileName The original filename in the user's filesystem, or
161      * <code>null</code> if not specified.
162      * @param sizeThreshold The threshold, in bytes, below which items will be
163      * retained in memory and above which they will be
164      * stored as a file.
165      * @param repository The data repository, which is the directory in
166      * which files will be created, should the item size
167      * exceed the threshold.
168      */

169     public DiskFileItem(String JavaDoc fieldName, String JavaDoc contentType,
170             boolean isFormField, String JavaDoc fileName, int sizeThreshold,
171             File JavaDoc repository) {
172         this.fieldName = fieldName;
173         this.contentType = contentType;
174         this.isFormField = isFormField;
175         this.fileName = fileName;
176         this.sizeThreshold = sizeThreshold;
177         this.repository = repository;
178     }
179
180
181     // ------------------------------- Methods from javax.activation.DataSource
182

183
184     /**
185      * Returns an {@link java.io.InputStream InputStream} that can be
186      * used to retrieve the contents of the file.
187      *
188      * @return An {@link java.io.InputStream InputStream} that can be
189      * used to retrieve the contents of the file.
190      *
191      * @throws IOException if an error occurs.
192      */

193     public InputStream JavaDoc getInputStream()
194         throws IOException JavaDoc {
195         if (!isInMemory()) {
196             return new FileInputStream JavaDoc(dfos.getFile());
197         }
198
199         if (cachedContent == null) {
200             cachedContent = dfos.getData();
201         }
202         return new ByteArrayInputStream JavaDoc(cachedContent);
203     }
204
205
206     /**
207      * Returns the content type passed by the agent or <code>null</code> if
208      * not defined.
209      *
210      * @return The content type passed by the agent or <code>null</code> if
211      * not defined.
212      */

213     public String JavaDoc getContentType() {
214         return contentType;
215     }
216
217
218     /**
219      * Returns the content charset passed by the agent or <code>null</code> if
220      * not defined.
221      *
222      * @return The content charset passed by the agent or <code>null</code> if
223      * not defined.
224      */

225     public String JavaDoc getCharSet() {
226         ParameterParser parser = new ParameterParser();
227         parser.setLowerCaseNames(true);
228         // Parameter parser can handle null input
229
Map JavaDoc params = parser.parse(getContentType(), ';');
230         return (String JavaDoc) params.get("charset");
231     }
232
233
234     /**
235      * Returns the original filename in the client's filesystem.
236      *
237      * @return The original filename in the client's filesystem.
238      */

239     public String JavaDoc getName() {
240         return fileName;
241     }
242
243
244     // ------------------------------------------------------- FileItem methods
245

246
247     /**
248      * Provides a hint as to whether or not the file contents will be read
249      * from memory.
250      *
251      * @return <code>true</code> if the file contents will be read
252      * from memory; <code>false</code> otherwise.
253      */

254     public boolean isInMemory() {
255         if (cachedContent != null) {
256             return true;
257         } else {
258             return dfos.isInMemory();
259         }
260     }
261
262
263     /**
264      * Returns the size of the file.
265      *
266      * @return The size of the file, in bytes.
267      */

268     public long getSize() {
269         if (cachedContent != null) {
270             return cachedContent.length;
271         } else if (dfos.isInMemory()) {
272             return dfos.getData().length;
273         } else {
274             return dfos.getFile().length();
275         }
276     }
277
278
279     /**
280      * Returns the contents of the file as an array of bytes. If the
281      * contents of the file were not yet cached in memory, they will be
282      * loaded from the disk storage and cached.
283      *
284      * @return The contents of the file as an array of bytes.
285      */

286     public byte[] get() {
287         if (isInMemory()) {
288             if (cachedContent == null) {
289                 cachedContent = dfos.getData();
290             }
291             return cachedContent;
292         }
293
294         byte[] fileData = new byte[(int) getSize()];
295         FileInputStream JavaDoc fis = null;
296
297         try {
298             fis = new FileInputStream JavaDoc(dfos.getFile());
299             fis.read(fileData);
300         } catch (IOException JavaDoc e) {
301             fileData = null;
302         } finally {
303             if (fis != null) {
304                 try {
305                     fis.close();
306                 } catch (IOException JavaDoc e) {
307                     // ignore
308
}
309             }
310         }
311
312         return fileData;
313     }
314
315
316     /**
317      * Returns the contents of the file as a String, using the specified
318      * encoding. This method uses {@link #get()} to retrieve the
319      * contents of the file.
320      *
321      * @param charset The charset to use.
322      *
323      * @return The contents of the file, as a string.
324      *
325      * @throws UnsupportedEncodingException if the requested character
326      * encoding is not available.
327      */

328     public String JavaDoc getString(final String JavaDoc charset)
329         throws UnsupportedEncodingException JavaDoc {
330         return new String JavaDoc(get(), charset);
331     }
332
333
334     /**
335      * Returns the contents of the file as a String, using the default
336      * character encoding. This method uses {@link #get()} to retrieve the
337      * contents of the file.
338      *
339      * @return The contents of the file, as a string.
340      *
341      * @todo Consider making this method throw UnsupportedEncodingException.
342      */

343     public String JavaDoc getString() {
344         byte[] rawdata = get();
345         String JavaDoc charset = getCharSet();
346         if (charset == null) {
347             charset = DEFAULT_CHARSET;
348         }
349         try {
350             return new String JavaDoc(rawdata, charset);
351         } catch (UnsupportedEncodingException JavaDoc e) {
352             return new String JavaDoc(rawdata);
353         }
354     }
355
356
357     /**
358      * A convenience method to write an uploaded item to disk. The client code
359      * is not concerned with whether or not the item is stored in memory, or on
360      * disk in a temporary location. They just want to write the uploaded item
361      * to a file.
362      * <p>
363      * This implementation first attempts to rename the uploaded item to the
364      * specified destination file, if the item was originally written to disk.
365      * Otherwise, the data will be copied to the specified file.
366      * <p>
367      * This method is only guaranteed to work <em>once</em>, the first time it
368      * is invoked for a particular item. This is because, in the event that the
369      * method renames a temporary file, that file will no longer be available
370      * to copy or rename again at a later time.
371      *
372      * @param file The <code>File</code> into which the uploaded item should
373      * be stored.
374      *
375      * @throws Exception if an error occurs.
376      */

377     public void write(File JavaDoc file) throws Exception JavaDoc {
378         if (isInMemory()) {
379             FileOutputStream JavaDoc fout = null;
380             try {
381                 fout = new FileOutputStream JavaDoc(file);
382                 fout.write(get());
383             } finally {
384                 if (fout != null) {
385                     fout.close();
386                 }
387             }
388         } else {
389             File JavaDoc outputFile = getStoreLocation();
390             if (outputFile != null) {
391                 /*
392                  * The uploaded file is being stored on disk
393                  * in a temporary location so move it to the
394                  * desired file.
395                  */

396                 if (!outputFile.renameTo(file)) {
397                     BufferedInputStream JavaDoc in = null;
398                     BufferedOutputStream JavaDoc out = null;
399                     try {
400                         in = new BufferedInputStream JavaDoc(
401                             new FileInputStream JavaDoc(outputFile));
402                         out = new BufferedOutputStream JavaDoc(
403                                 new FileOutputStream JavaDoc(file));
404                         IOUtils.copy(in, out);
405                     } finally {
406                         if (in != null) {
407                             try {
408                                 in.close();
409                             } catch (IOException JavaDoc e) {
410                                 // ignore
411
}
412                         }
413                         if (out != null) {
414                             try {
415                                 out.close();
416                             } catch (IOException JavaDoc e) {
417                                 // ignore
418
}
419                         }
420                     }
421                 }
422             } else {
423                 /*
424                  * For whatever reason we cannot write the
425                  * file to disk.
426                  */

427                 throw new FileUploadException(
428                     "Cannot write uploaded file to disk!");
429             }
430         }
431     }
432
433
434     /**
435      * Deletes the underlying storage for a file item, including deleting any
436      * associated temporary disk file. Although this storage will be deleted
437      * automatically when the <code>FileItem</code> instance is garbage
438      * collected, this method can be used to ensure that this is done at an
439      * earlier time, thus preserving system resources.
440      */

441     public void delete() {
442         cachedContent = null;
443         File JavaDoc outputFile = getStoreLocation();
444         if (outputFile != null && outputFile.exists()) {
445             outputFile.delete();
446         }
447     }
448
449
450     /**
451      * Returns the name of the field in the multipart form corresponding to
452      * this file item.
453      *
454      * @return The name of the form field.
455      *
456      * @see #setFieldName(java.lang.String)
457      *
458      */

459     public String JavaDoc getFieldName() {
460         return fieldName;
461     }
462
463
464     /**
465      * Sets the field name used to reference this file item.
466      *
467      * @param fieldName The name of the form field.
468      *
469      * @see #getFieldName()
470      *
471      */

472     public void setFieldName(String JavaDoc fieldName) {
473         this.fieldName = fieldName;
474     }
475
476
477     /**
478      * Determines whether or not a <code>FileItem</code> instance represents
479      * a simple form field.
480      *
481      * @return <code>true</code> if the instance represents a simple form
482      * field; <code>false</code> if it represents an uploaded file.
483      *
484      * @see #setFormField(boolean)
485      *
486      */

487     public boolean isFormField() {
488         return isFormField;
489     }
490
491
492     /**
493      * Specifies whether or not a <code>FileItem</code> instance represents
494      * a simple form field.
495      *
496      * @param state <code>true</code> if the instance represents a simple form
497      * field; <code>false</code> if it represents an uploaded file.
498      *
499      * @see #isFormField()
500      *
501      */

502     public void setFormField(boolean state) {
503         isFormField = state;
504     }
505
506
507     /**
508      * Returns an {@link java.io.OutputStream OutputStream} that can
509      * be used for storing the contents of the file.
510      *
511      * @return An {@link java.io.OutputStream OutputStream} that can be used
512      * for storing the contensts of the file.
513      *
514      * @throws IOException if an error occurs.
515      */

516     public OutputStream JavaDoc getOutputStream()
517         throws IOException JavaDoc {
518         if (dfos == null) {
519             File JavaDoc outputFile = getTempFile();
520             dfos = new DeferredFileOutputStream(sizeThreshold, outputFile);
521         }
522         return dfos;
523     }
524
525
526     // --------------------------------------------------------- Public methods
527

528
529     /**
530      * Returns the {@link java.io.File} object for the <code>FileItem</code>'s
531      * data's temporary location on the disk. Note that for
532      * <code>FileItem</code>s that have their data stored in memory,
533      * this method will return <code>null</code>. When handling large
534      * files, you can use {@link java.io.File#renameTo(java.io.File)} to
535      * move the file to new location without copying the data, if the
536      * source and destination locations reside within the same logical
537      * volume.
538      *
539      * @return The data file, or <code>null</code> if the data is stored in
540      * memory.
541      */

542     public File JavaDoc getStoreLocation() {
543         return dfos.getFile();
544     }
545
546
547     // ------------------------------------------------------ Protected methods
548

549
550     /**
551      * Removes the file contents from the temporary storage.
552      */

553     protected void finalize() {
554         File JavaDoc outputFile = dfos.getFile();
555
556         if (outputFile != null && outputFile.exists()) {
557             outputFile.delete();
558         }
559     }
560
561
562     /**
563      * Creates and returns a {@link java.io.File File} representing a uniquely
564      * named temporary file in the configured repository path. The lifetime of
565      * the file is tied to the lifetime of the <code>FileItem</code> instance;
566      * the file will be deleted when the instance is garbage collected.
567      *
568      * @return The {@link java.io.File File} to be used for temporary storage.
569      */

570     protected File JavaDoc getTempFile() {
571         File JavaDoc tempDir = repository;
572         if (tempDir == null) {
573             tempDir = new File JavaDoc(System.getProperty("java.io.tmpdir"));
574         }
575
576         String JavaDoc fileName = "upload_" + UID + "_" + getUniqueId() + ".tmp";
577
578         File JavaDoc f = new File JavaDoc(tempDir, fileName);
579         FileCleaner.track(f, this);
580         return f;
581     }
582
583
584     // -------------------------------------------------------- Private methods
585

586
587     /**
588      * Returns an identifier that is unique within the class loader used to
589      * load this class, but does not have random-like apearance.
590      *
591      * @return A String with the non-random looking instance identifier.
592      */

593     private static String JavaDoc getUniqueId() {
594         final int limit = 100000000;
595         int current;
596         synchronized (DiskFileItem.class) {
597             current = counter++;
598         }
599         String JavaDoc id = Integer.toString(current);
600
601         // If you manage to get more than 100 million of ids, you'll
602
// start getting ids longer than 8 characters.
603
if (current < limit) {
604             id = ("00000000" + id).substring(id.length());
605         }
606         return id;
607     }
608
609
610
611
612     /**
613      * Returns a string representation of this object.
614      *
615      * @return a string representation of this object.
616      */

617     public String JavaDoc toString() {
618         return "name=" + this.getName()
619             + ", StoreLocation="
620             + String.valueOf(this.getStoreLocation())
621             + ", size="
622             + this.getSize()
623             + "bytes, "
624             + "isFormField=" + isFormField()
625             + ", FieldName="
626             + this.getFieldName();
627     }
628
629
630     // -------------------------------------------------- Serialization methods
631

632
633     /**
634      * Writes the state of this object during serialization.
635      *
636      * @param out The stream to which the state should be written.
637      *
638      * @throws IOException if an error occurs.
639      */

640     private void writeObject(ObjectOutputStream JavaDoc out) throws IOException JavaDoc {
641         // Read the data
642
if (dfos.isInMemory()) {
643             cachedContent = get();
644         } else {
645             cachedContent = null;
646             dfosFile = dfos.getFile();
647         }
648
649         // write out values
650
out.defaultWriteObject();
651     }
652
653     /**
654      * Reads the state of this object during deserialization.
655      *
656      * @param in The stream from which the state should be read.
657      *
658      * @throws IOException if an error occurs.
659      * @throws ClassNotFoundException if class cannot be found.
660      */

661     private void readObject(ObjectInputStream JavaDoc in)
662             throws IOException JavaDoc, ClassNotFoundException JavaDoc {
663         // read values
664
in.defaultReadObject();
665
666         OutputStream JavaDoc output = getOutputStream();
667         if (cachedContent != null) {
668             output.write(cachedContent);
669         } else {
670             FileInputStream JavaDoc input = new FileInputStream JavaDoc(dfosFile);
671
672             IOUtils.copy(input, output);
673             dfosFile.delete();
674             dfosFile = null;
675         }
676         output.close();
677
678         cachedContent = null;
679     }
680
681 }
682
Popular Tags