001: package de.ug2t.extTools.httpFileUpLoad;
002:
003: /*
004: * $Header: /home/cvs/jakarta-commons/fileupload/src/java/org/apache/commons/fileupload/DefaultFileItem.java,v 1.21 2003/06/24 05:45:15 martinc Exp $
005: * $Revision: 1.21 $
006: * $Date: 2003/06/24 05:45:15 $
007: *
008: * ====================================================================
009: *
010: * The Apache Software License, Version 1.1
011: *
012: * Copyright (c) 2001-2003 The Apache Software Foundation. All rights
013: * reserved.
014: *
015: * Redistribution and use in source and binary forms, with or without
016: * modification, are permitted provided that the following conditions
017: * are met:
018: *
019: * 1. Redistributions of source code must retain the above copyright
020: * notice, this list of conditions and the following disclaimer.
021: *
022: * 2. Redistributions in binary form must reproduce the above copyright
023: * notice, this list of conditions and the following disclaimer in
024: * the documentation and/or other materials provided with the
025: * distribution.
026: *
027: * 3. The end-user documentation included with the redistribution, if
028: * any, must include the following acknowlegement:
029: * "This product includes software developed by the
030: * Apache Software Foundation (http://www.apache.org/)."
031: * Alternately, this acknowlegement may appear in the software itself,
032: * if and wherever such third-party acknowlegements normally appear.
033: *
034: * 4. The names "The Jakarta Project", "Commons", and "Apache Software
035: * Foundation" must not be used to endorse or promote products derived
036: * from this software without prior written permission. For written
037: * permission, please contact apache@apache.org.
038: *
039: * 5. Products derived from this software may not be called "Apache"
040: * nor may "Apache" appear in their names without prior written
041: * permission of the Apache Group.
042: *
043: * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
044: * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
045: * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
046: * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
047: * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
048: * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
049: * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
050: * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
051: * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
052: * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
053: * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
054: * SUCH DAMAGE.
055: * ====================================================================
056: *
057: * This software consists of voluntary contributions made by many
058: * individuals on behalf of the Apache Software Foundation. For more
059: * information on the Apache Software Foundation, please see
060: * <http://www.apache.org/>.
061: *
062: */
063:
064: import java.io.*;
065:
066: /**
067: * <p>
068: * The default implementation of the
069: * {@link org.apache.commons.fileupload.FileItem FileItem} interface.
070: *
071: * <p>
072: * After retrieving an instance of this class from a {@link
073: * org.apache.commons.fileupload.DiskFileUpload DiskFileUpload} instance (see
074: * {@link org.apache.commons.fileupload.DiskFileUpload
075: * #parseRequest(javax.servlet.http.HttpServletRequest)}), you may either
076: * request all contents of file at once using {@link #get()} or request an
077: * {@link java.io.InputStream InputStream} with {@link #getInputStream()} and
078: * process the file without attempting to load it into memory, which may come
079: * handy with large files.
080: *
081: * @author <a href="mailto:Rafal.Krzewski@e-point.pl">Rafal Krzewski</a>
082: * @author <a href="mailto:sean@informage.net">Sean Legassick</a>
083: * @author <a href="mailto:jvanzyl@apache.org">Jason van Zyl</a>
084: * @author <a href="mailto:jmcnally@apache.org">John McNally</a>
085: * @author <a href="mailto:martinc@apache.org">Martin Cooper</a>
086: * @author Sean C. Sullivan
087: *
088: * @version $Id: DefaultFileItem.java,v 1.21 2003/06/24 05:45:15 martinc Exp $
089: */
090: public class DefaultFileItem implements FileItem {
091:
092: // ----------------------------------------------------------- Data members
093:
094: /**
095: * Counter used in unique identifier generation.
096: */
097: private static int counter = 0;
098:
099: /**
100: * The name of the form field as provided by the browser.
101: */
102: private String fieldName;
103:
104: /**
105: * The content type passed by the browser, or <code>null</code> if not
106: * defined.
107: */
108: private String contentType;
109:
110: /**
111: * Whether or not this item is a simple form field.
112: */
113: private boolean isFormField;
114:
115: /**
116: * The original filename in the user's filesystem.
117: */
118: private String fileName;
119:
120: /**
121: * The threshold above which uploads will be stored on disk.
122: */
123: private int sizeThreshold;
124:
125: /**
126: * The directory in which uploaded files will be stored, if stored on disk.
127: */
128: private File repository;
129:
130: /**
131: * Cached contents of the file.
132: */
133: private byte[] cachedContent;
134:
135: /**
136: * Output stream for this item.
137: */
138: private DeferredFileOutputStream dfos;
139:
140: // ----------------------------------------------------------- Constructors
141:
142: /**
143: * Constructs a new <code>DefaultFileItem</code> instance.
144: *
145: * @param fieldName
146: * The name of the form field.
147: * @param contentType
148: * The content type passed by the browser or <code>null</code> if
149: * not specified.
150: * @param isFormField
151: * Whether or not this item is a plain form field, as opposed to a
152: * file upload.
153: * @param fileName
154: * The original filename in the user's filesystem, or
155: * <code>null</code> if not specified.
156: * @param sizeThreshold
157: * The threshold, in bytes, below which items will be retained in
158: * memory and above which they will be stored as a file.
159: * @param repository
160: * The data repository, which is the directory in which files will be
161: * created, should the item size exceed the threshold.
162: */
163: DefaultFileItem(String fieldName, String contentType,
164: boolean isFormField, String fileName, int sizeThreshold,
165: File repository) {
166: this .fieldName = fieldName;
167: this .contentType = contentType;
168: this .isFormField = isFormField;
169: this .fileName = fileName;
170: this .sizeThreshold = sizeThreshold;
171: this .repository = repository;
172: }
173:
174: // ------------------------------- Methods from javax.activation.DataSource
175:
176: /**
177: * Returns an {@link java.io.InputStream InputStream} that can be used to
178: * retrieve the contents of the file.
179: *
180: * @return An {@link java.io.InputStream InputStream} that can be used to
181: * retrieve the contents of the file.
182: *
183: * @exception IOException
184: * if an error occurs.
185: */
186: public InputStream getInputStream() throws IOException {
187: if (!dfos.isInMemory()) {
188: return new FileInputStream(dfos.getFile());
189: }
190:
191: if (cachedContent == null) {
192: cachedContent = dfos.getData();
193: }
194: return new ByteArrayInputStream(cachedContent);
195: }
196:
197: /**
198: * Returns the content type passed by the browser or <code>null</code> if
199: * not defined.
200: *
201: * @return The content type passed by the browser or <code>null</code> if
202: * not defined.
203: */
204: public String getContentType() {
205: return contentType;
206: }
207:
208: /**
209: * Returns the original filename in the client's filesystem.
210: *
211: * @return The original filename in the client's filesystem.
212: */
213: public String getName() {
214: return fileName;
215: }
216:
217: // ------------------------------------------------------- FileItem methods
218:
219: /**
220: * Provides a hint as to whether or not the file contents will be read from
221: * memory.
222: *
223: * @return <code>true</code> if the file contents will be read from memory;
224: * <code>false</code> otherwise.
225: */
226: public boolean isInMemory() {
227: return (dfos.isInMemory());
228: }
229:
230: /**
231: * Returns the size of the file.
232: *
233: * @return The size of the file, in bytes.
234: */
235: public long getSize() {
236: if (cachedContent != null) {
237: return cachedContent.length;
238: } else if (dfos.isInMemory()) {
239: return dfos.getData().length;
240: } else {
241: return dfos.getFile().length();
242: }
243: }
244:
245: /**
246: * Returns the contents of the file as an array of bytes. If the contents of
247: * the file were not yet cached in memory, they will be loaded from the disk
248: * storage and cached.
249: *
250: * @return The contents of the file as an array of bytes.
251: */
252: public byte[] get() {
253: if (dfos.isInMemory()) {
254: if (cachedContent == null) {
255: cachedContent = dfos.getData();
256: }
257: return cachedContent;
258: }
259:
260: byte[] fileData = new byte[(int) getSize()];
261: FileInputStream fis = null;
262:
263: try {
264: fis = new FileInputStream(dfos.getFile());
265: fis.read(fileData);
266: } catch (IOException e) {
267: fileData = null;
268: } finally {
269: if (fis != null) {
270: try {
271: fis.close();
272: } catch (IOException e) {
273: // ignore
274: }
275: }
276: }
277:
278: return fileData;
279: }
280:
281: /**
282: * Returns the contents of the file as a String, using the specified encoding.
283: * This method uses {@link #get()} to retrieve the contents of the file.
284: *
285: * @param encoding
286: * The character encoding to use.
287: *
288: * @return The contents of the file, as a string.
289: *
290: * @exception UnsupportedEncodingException
291: * if the requested character encoding is not available.
292: */
293: public String getString(String encoding)
294: throws UnsupportedEncodingException {
295: return new String(get(), encoding);
296: }
297:
298: /**
299: * Returns the contents of the file as a String, using the default character
300: * encoding. This method uses {@link #get()} to retrieve the contents of the
301: * file.
302: *
303: * @return The contents of the file, as a string.
304: */
305: public String getString() {
306: return new String(get());
307: }
308:
309: /**
310: * A convenience method to write an uploaded item to disk. The client code is
311: * not concerned with whether or not the item is stored in memory, or on disk
312: * in a temporary location. They just want to write the uploaded item to a
313: * file.
314: * <p>
315: * This implementation first attempts to rename the uploaded item to the
316: * specified destination file, if the item was originally written to disk.
317: * Otherwise, the data will be copied to the specified file.
318: * <p>
319: * This method is only guaranteed to Work <em>once</em>, the first time it
320: * is invoked for a particular item. This is because, in the event that the
321: * method renames a temporary file, that file will no longer be available to
322: * copy or rename again at a later time.
323: *
324: * @param file
325: * The <code>File</code> into which the uploaded item should be
326: * stored.
327: *
328: * @exception Exception
329: * if an error occurs.
330: */
331: public void write(File file) throws Exception {
332: if (isInMemory()) {
333: FileOutputStream fout = null;
334: try {
335: fout = new FileOutputStream(file);
336: fout.write(get());
337: } finally {
338: if (fout != null) {
339: fout.close();
340: }
341: }
342: } else {
343: File outputFile = getStoreLocation();
344: if (outputFile != null) {
345: /*
346: * The uploaded file is being stored on disk in a temporary location so
347: * move it to the desired file.
348: */
349: if (!outputFile.renameTo(file)) {
350: BufferedInputStream in = null;
351: BufferedOutputStream out = null;
352: try {
353: in = new BufferedInputStream(
354: new FileInputStream(outputFile));
355: out = new BufferedOutputStream(
356: new FileOutputStream(file));
357: byte[] bytes = new byte[2048];
358: int s = 0;
359: while ((s = in.read(bytes)) != -1) {
360: out.write(bytes, 0, s);
361: }
362: } finally {
363: try {
364: in.close();
365: } catch (IOException e) {
366: // ignore
367: }
368: try {
369: out.close();
370: } catch (IOException e) {
371: // ignore
372: }
373: }
374: }
375: } else {
376: /*
377: * For whatever reason we cannot write the file to disk.
378: */
379: throw new FileUploadException(
380: "Cannot write uploaded file to disk!");
381: }
382: }
383: }
384:
385: /**
386: * Deletes the underlying storage for a file item, including deleting any
387: * associated temporary disk file. Although this storage will be deleted
388: * automatically when the <code>FileItem</code> instance is garbage
389: * collected, this method can be used to ensure that this is done at an
390: * earlier time, thus preserving system resources.
391: */
392: public void delete() {
393: cachedContent = null;
394: File outputFile = getStoreLocation();
395: if (outputFile != null && outputFile.exists()) {
396: outputFile.delete();
397: }
398: }
399:
400: /**
401: * Returns the name of the field in the multipart form corresponding to this
402: * file item.
403: *
404: * @return The name of the form field.
405: *
406: * @see #setFieldName(java.lang.String)
407: *
408: */
409: public String getFieldName() {
410: return fieldName;
411: }
412:
413: /**
414: * Sets the field name used to reference this file item.
415: *
416: * @param fieldName
417: * The name of the form field.
418: *
419: * @see #getFieldName()
420: *
421: */
422: public void setFieldName(String fieldName) {
423: this .fieldName = fieldName;
424: }
425:
426: /**
427: * Determines whether or not a <code>FileItem</code> instance represents a
428: * simple form field.
429: *
430: * @return <code>true</code> if the instance represents a simple form field;
431: * <code>false</code> if it represents an uploaded file.
432: *
433: * @see #setFormField(boolean)
434: *
435: */
436: public boolean isFormField() {
437: return isFormField;
438: }
439:
440: /**
441: * Specifies whether or not a <code>FileItem</code> instance represents a
442: * simple form field.
443: *
444: * @param state
445: * <code>true</code> if the instance represents a simple form
446: * field; <code>false</code> if it represents an uploaded file.
447: *
448: * @see #isFormField()
449: *
450: */
451: public void setFormField(boolean state) {
452: isFormField = state;
453: }
454:
455: /**
456: * Returns an {@link java.io.OutputStream OutputStream} that can be used for
457: * storing the contents of the file.
458: *
459: * @return An {@link java.io.OutputStream OutputStream} that can be used for
460: * storing the contensts of the file.
461: *
462: * @exception IOException
463: * if an error occurs.
464: */
465: public OutputStream getOutputStream() throws IOException {
466: if (dfos == null) {
467: File outputFile = getTempFile();
468: dfos = new DeferredFileOutputStream(sizeThreshold,
469: outputFile);
470: }
471: return dfos;
472: }
473:
474: // --------------------------------------------------------- Public methods
475:
476: /**
477: * Returns the {@link java.io.File} object for the <code>FileItem</code>'s
478: * data's temporary location on the disk. Note that for <code>FileItem</code>s
479: * that have their data stored in memory, this method will return
480: * <code>null</code>. When handling large files, you can use
481: * {@link java.io.File#renameTo(java.io.File)} to move the file to new
482: * location without copying the data, if the source and destination locations
483: * reside within the same logical volume.
484: *
485: * @return The data file, or <code>null</code> if the data is stored in
486: * memory.
487: */
488: public File getStoreLocation() {
489: return dfos.getFile();
490: }
491:
492: // ------------------------------------------------------ Protected methods
493:
494: /**
495: * Removes the file contents from the temporary storage.
496: */
497: protected void finalize() {
498: File outputFile = dfos.getFile();
499:
500: if (outputFile != null && outputFile.exists()) {
501: outputFile.delete();
502: }
503: }
504:
505: /**
506: * Creates and returns a {@link java.io.File File} representing a uniquely
507: * named temporary file in the configured repository path.
508: *
509: * @return The {@link java.io.File File} to be used for temporary storage.
510: */
511: protected File getTempFile() {
512: File tempDir = repository;
513: if (tempDir == null) {
514: tempDir = new File(System.getProperty("java.io.tmpdir"));
515: }
516:
517: String fileName = "upload_" + getUniqueId() + ".tmp";
518:
519: File f = new File(tempDir, fileName);
520: f.deleteOnExit();
521: return f;
522: }
523:
524: // -------------------------------------------------------- Private methods
525:
526: /**
527: * Returns an identifier that is unique within the class loader used to load
528: * this class, but does not have random-like apearance.
529: *
530: * @return A String with the non-random looking instance identifier.
531: */
532: private static String getUniqueId() {
533: int current;
534: synchronized (DefaultFileItem.class) {
535: current = counter++;
536: }
537: String id = Integer.toString(current);
538:
539: // If you manage to get more than 100 million of ids, you'll
540: // start getting ids longer than 8 characters.
541: if (current < 100000000) {
542: id = ("00000000" + id).substring(id.length());
543: }
544: return id;
545: }
546:
547: }
|