KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > net > jforum > util > legacy > commons > fileupload > disk > DiskFileItem


1 /*
2  * Copyright 2001-2004 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.jforum.util.legacy.commons.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.UnsupportedEncodingException JavaDoc;
28 import java.util.Map JavaDoc;
29
30 import net.jforum.util.legacy.commons.fileupload.FileItem;
31 import net.jforum.util.legacy.commons.fileupload.FileUploadException;
32 import net.jforum.util.legacy.commons.fileupload.ParameterParser;
33
34 import org.apache.commons.io.FileCleaner;
35 import org.apache.commons.io.output.DeferredFileOutputStream;
36
37
38 /**
39  * <p> The default implementation of the
40  * {@link org.apache.commons.fileupload.FileItem FileItem} interface.
41  *
42  * <p> After retrieving an instance of this class from a {@link
43  * org.apache.commons.fileupload.DiskFileUpload DiskFileUpload} instance (see
44  * {@link org.apache.commons.fileupload.DiskFileUpload
45  * #parseRequest(javax.servlet.http.HttpServletRequest)}), you may
46  * either request all contents of file at once using {@link #get()} or
47  * request an {@link java.io.InputStream InputStream} with
48  * {@link #getInputStream()} and process the file without attempting to load
49  * it into memory, which may come handy with large files.
50  *
51  * @author <a HREF="mailto:Rafal.Krzewski@e-point.pl">Rafal Krzewski</a>
52  * @author <a HREF="mailto:sean@informage.net">Sean Legassick</a>
53  * @author <a HREF="mailto:jvanzyl@apache.org">Jason van Zyl</a>
54  * @author <a HREF="mailto:jmcnally@apache.org">John McNally</a>
55  * @author <a HREF="mailto:martinc@apache.org">Martin Cooper</a>
56  * @author Sean C. Sullivan
57  *
58  * @since FileUpload 1.1
59  *
60  * @version $Id: DiskFileItem.java,v 1.3 2005/07/26 03:05:02 rafaelsteil Exp $
61  */

62 public class DiskFileItem
63     implements FileItem {
64
65     // ----------------------------------------------------- Manifest constants
66

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

74     public static final String JavaDoc DEFAULT_CHARSET = "ISO-8859-1";
75
76
77     /**
78      * Size of buffer to use when writing an item to disk.
79      */

80     private static final int WRITE_BUFFER_SIZE = 2048;
81
82
83     // ----------------------------------------------------------- Data members
84

85
86     /**
87      * Counter used in unique identifier generation.
88      */

89     private static int counter = 0;
90
91
92     /**
93      * The name of the form field as provided by the browser.
94      */

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

102     private String JavaDoc contentType;
103
104
105     /**
106      * Whether or not this item is a simple form field.
107      */

108     private boolean isFormField;
109
110
111     /**
112      * The original filename in the user's filesystem.
113      */

114     private String JavaDoc fileName;
115
116
117     /**
118      * The threshold above which uploads will be stored on disk.
119      */

120     private int sizeThreshold;
121
122
123     /**
124      * The directory in which uploaded files will be stored, if stored on disk.
125      */

126     private File JavaDoc repository;
127
128
129     /**
130      * Cached contents of the file.
131      */

132     private byte[] cachedContent;
133
134
135     /**
136      * Output stream for this item.
137      */

138     private DeferredFileOutputStream dfos;
139
140
141     // ----------------------------------------------------------- Constructors
142

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

161     public DiskFileItem(String JavaDoc fieldName, String JavaDoc contentType,
162             boolean isFormField, String JavaDoc fileName, int sizeThreshold,
163             File JavaDoc repository) {
164         this.fieldName = fieldName;
165         this.contentType = contentType;
166         this.isFormField = isFormField;
167         this.fileName = fileName;
168         this.sizeThreshold = sizeThreshold;
169         this.repository = repository;
170     }
171
172
173     // ------------------------------- Methods from javax.activation.DataSource
174

175
176     /**
177      * Returns an {@link java.io.InputStream InputStream} that can be
178      * used to retrieve the contents of the file.
179      *
180      * @return An {@link java.io.InputStream InputStream} that can be
181      * used to retrieve the contents of the file.
182      *
183      * @exception IOException if an error occurs.
184      */

185     public InputStream JavaDoc getInputStream()
186         throws IOException JavaDoc {
187         if (!dfos.isInMemory()) {
188             return new FileInputStream JavaDoc(dfos.getFile());
189         }
190
191         if (cachedContent == null) {
192             cachedContent = dfos.getData();
193         }
194         return new ByteArrayInputStream JavaDoc(cachedContent);
195     }
196
197
198     /**
199      * Returns the content type passed by the agent or <code>null</code> if
200      * not defined.
201      *
202      * @return The content type passed by the agent or <code>null</code> if
203      * not defined.
204      */

205     public String JavaDoc getContentType() {
206         return contentType;
207     }
208
209
210     /**
211      * Returns the content charset passed by the agent or <code>null</code> if
212      * not defined.
213      *
214      * @return The content charset passed by the agent or <code>null</code> if
215      * not defined.
216      */

217     public String JavaDoc getCharSet() {
218         ParameterParser parser = new ParameterParser();
219         parser.setLowerCaseNames(true);
220         // Parameter parser can handle null input
221
Map JavaDoc params = parser.parse(getContentType(), ';');
222         return (String JavaDoc) params.get("charset");
223     }
224
225
226     /**
227      * Returns the original filename in the client's filesystem.
228      *
229      * @return The original filename in the client's filesystem.
230      */

231     public String JavaDoc getName() {
232         return fileName;
233     }
234
235
236     // ------------------------------------------------------- FileItem methods
237

238
239     /**
240      * Provides a hint as to whether or not the file contents will be read
241      * from memory.
242      *
243      * @return <code>true</code> if the file contents will be read
244      * from memory; <code>false</code> otherwise.
245      */

246     public boolean isInMemory() {
247         return (dfos.isInMemory());
248     }
249
250
251     /**
252      * Returns the size of the file.
253      *
254      * @return The size of the file, in bytes.
255      */

256     public long getSize() {
257         if (cachedContent != null) {
258             return cachedContent.length;
259         } else if (dfos.isInMemory()) {
260             return dfos.getData().length;
261         } else {
262             return dfos.getFile().length();
263         }
264     }
265
266
267     /**
268      * Returns the contents of the file as an array of bytes. If the
269      * contents of the file were not yet cached in memory, they will be
270      * loaded from the disk storage and cached.
271      *
272      * @return The contents of the file as an array of bytes.
273      */

274     public byte[] get() {
275         if (dfos.isInMemory()) {
276             if (cachedContent == null) {
277                 cachedContent = dfos.getData();
278             }
279             return cachedContent;
280         }
281
282         byte[] fileData = new byte[(int) getSize()];
283         FileInputStream JavaDoc fis = null;
284
285         try {
286             fis = new FileInputStream JavaDoc(dfos.getFile());
287             fis.read(fileData);
288         } catch (IOException JavaDoc e) {
289             fileData = null;
290         } finally {
291             if (fis != null) {
292                 try {
293                     fis.close();
294                 } catch (IOException JavaDoc e) {
295                     // ignore
296
}
297             }
298         }
299
300         return fileData;
301     }
302
303
304     /**
305      * Returns the contents of the file as a String, using the specified
306      * encoding. This method uses {@link #get()} to retrieve the
307      * contents of the file.
308      *
309      * @param charset The charset to use.
310      *
311      * @return The contents of the file, as a string.
312      *
313      * @exception UnsupportedEncodingException if the requested character
314      * encoding is not available.
315      */

316     public String JavaDoc getString(final String JavaDoc charset)
317         throws UnsupportedEncodingException JavaDoc {
318         return new String JavaDoc(get(), charset);
319     }
320
321
322     /**
323      * Returns the contents of the file as a String, using the default
324      * character encoding. This method uses {@link #get()} to retrieve the
325      * contents of the file.
326      *
327      * @return The contents of the file, as a string.
328      *
329      * @todo Consider making this method throw UnsupportedEncodingException.
330      */

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

365     public void write(File JavaDoc file) throws Exception JavaDoc {
366         if (isInMemory()) {
367             FileOutputStream JavaDoc fout = null;
368             try {
369                 fout = new FileOutputStream JavaDoc(file);
370                 fout.write(get());
371             } finally {
372                 if (fout != null) {
373                     fout.close();
374                 }
375             }
376         } else {
377             File JavaDoc outputFile = getStoreLocation();
378             if (outputFile != null) {
379                 /*
380                  * The uploaded file is being stored on disk
381                  * in a temporary location so move it to the
382                  * desired file.
383                  */

384                 if (!outputFile.renameTo(file)) {
385                     BufferedInputStream JavaDoc in = null;
386                     BufferedOutputStream JavaDoc out = null;
387                     try {
388                         in = new BufferedInputStream JavaDoc(
389                             new FileInputStream JavaDoc(outputFile));
390                         out = new BufferedOutputStream JavaDoc(
391                                 new FileOutputStream JavaDoc(file));
392                         byte[] bytes = new byte[WRITE_BUFFER_SIZE];
393                         int s = 0;
394                         while ((s = in.read(bytes)) != -1) {
395                             out.write(bytes, 0, s);
396                         }
397                     } finally {
398                         if (in != null) {
399                             try {
400                                 in.close();
401                             } catch (IOException JavaDoc e) {
402                                 // ignore
403
}
404                         }
405                         if (out != null) {
406                             try {
407                                 out.close();
408                             } catch (IOException JavaDoc e) {
409                                 // ignore
410
}
411                         }
412                     }
413                 }
414             } else {
415                 /*
416                  * For whatever reason we cannot write the
417                  * file to disk.
418                  */

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

433     public void delete() {
434         cachedContent = null;
435         File JavaDoc outputFile = getStoreLocation();
436         if (outputFile != null && outputFile.exists()) {
437             outputFile.delete();
438         }
439     }
440
441
442     /**
443      * Returns the name of the field in the multipart form corresponding to
444      * this file item.
445      *
446      * @return The name of the form field.
447      *
448      * @see #setFieldName(java.lang.String)
449      *
450      */

451     public String JavaDoc getFieldName() {
452         return fieldName;
453     }
454
455
456     /**
457      * Sets the field name used to reference this file item.
458      *
459      * @param fieldName The name of the form field.
460      *
461      * @see #getFieldName()
462      *
463      */

464     public void setFieldName(String JavaDoc fieldName) {
465         this.fieldName = fieldName;
466     }
467
468
469     /**
470      * Determines whether or not a <code>FileItem</code> instance represents
471      * a simple form field.
472      *
473      * @return <code>true</code> if the instance represents a simple form
474      * field; <code>false</code> if it represents an uploaded file.
475      *
476      * @see #setFormField(boolean)
477      *
478      */

479     public boolean isFormField() {
480         return isFormField;
481     }
482
483
484     /**
485      * Specifies whether or not a <code>FileItem</code> instance represents
486      * a simple form field.
487      *
488      * @param state <code>true</code> if the instance represents a simple form
489      * field; <code>false</code> if it represents an uploaded file.
490      *
491      * @see #isFormField()
492      *
493      */

494     public void setFormField(boolean state) {
495         isFormField = state;
496     }
497
498
499     /**
500      * Returns an {@link java.io.OutputStream OutputStream} that can
501      * be used for storing the contents of the file.
502      *
503      * @return An {@link java.io.OutputStream OutputStream} that can be used
504      * for storing the contensts of the file.
505      *
506      * @exception IOException if an error occurs.
507      */

508     public OutputStream JavaDoc getOutputStream()
509         throws IOException JavaDoc {
510         if (dfos == null) {
511             File JavaDoc outputFile = getTempFile();
512             dfos = new DeferredFileOutputStream(sizeThreshold, outputFile);
513         }
514         return dfos;
515     }
516
517
518     // --------------------------------------------------------- Public methods
519

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

534     public File JavaDoc getStoreLocation() {
535         return dfos.getFile();
536     }
537
538
539     // ------------------------------------------------------ Protected methods
540

541
542     /**
543      * Removes the file contents from the temporary storage.
544      */

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

562     protected File JavaDoc getTempFile() {
563         File JavaDoc tempDir = repository;
564         if (tempDir == null) {
565             tempDir = new File JavaDoc(System.getProperty("java.io.tmpdir"));
566         }
567
568         String JavaDoc fileName = "upload_" + getUniqueId() + ".tmp";
569
570         File JavaDoc f = new File JavaDoc(tempDir, fileName);
571         FileCleaner.track(f, this);
572         return f;
573     }
574
575
576     // -------------------------------------------------------- Private methods
577

578
579     /**
580      * Returns an identifier that is unique within the class loader used to
581      * load this class, but does not have random-like apearance.
582      *
583      * @return A String with the non-random looking instance identifier.
584      */

585     private static String JavaDoc getUniqueId() {
586         int current;
587         synchronized (DiskFileItem.class) {
588             current = counter++;
589         }
590         String JavaDoc id = Integer.toString(current);
591
592         // If you manage to get more than 100 million of ids, you'll
593
// start getting ids longer than 8 characters.
594
if (current < 100000000) {
595             id = ("00000000" + id).substring(id.length());
596         }
597         return id;
598     }
599     
600     public String JavaDoc toString() {
601         return "name=" + this.getName()
602             + ", StoreLocation="
603             + String.valueOf(this.getStoreLocation())
604             + ", size="
605             + this.getSize()
606             + "bytes, "
607             + "isFormField=" + isFormField()
608             + ", FieldName="
609             + this.getFieldName();
610     }
611
612 }
613
Popular Tags