com.w4t.engine.requests.UploadRequestFileItem.java Source code

Java tutorial

Introduction

Here is the source code for com.w4t.engine.requests.UploadRequestFileItem.java

Source

/*******************************************************************************
 * Copyright (c) 2002-2006 Innoopract Informationssysteme GmbH.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 * 
 * Contributors:
 *     Innoopract Informationssysteme GmbH - initial API and implementation
 ******************************************************************************/
package com.w4t.engine.requests;

import java.io.*;
import org.apache.commons.fileupload.DeferredFileOutputStream;
import org.apache.commons.fileupload.FileItem;

/**
 * <p>
 * The FileItem implementation of W4Toolkit provides some optimizations
 * regarding the memory footprint of formfields items.
 * </p>
 */
class UploadRequestFileItem implements FileItem {

    private static final long serialVersionUID = 1L;

    private static final int DEFAULT_OUTPUTSTREAM_SIZE = 32;

    private static int counter = 0;
    private String fieldName;
    private String contentType;
    private boolean isFormField;
    private String fileName;
    private int sizeThreshold;
    private File repository;
    private byte[] cachedContent;
    private DeferredFileOutputStream dfos;

    /**
     * <p>creates a new <code>UploadRequestFileItem</code> instance.</p>
     * 
     * @param fieldName The name of the form field.
     * @param contentType The content type passed by the browser or
     *        <code>null</code> if not specified.
     * @param isFormField Whether or not this item is a plain form field, as
     *        opposed to a file upload.
     * @param fileName The original filename in the user's filesystem, or
     *        <code>null</code> if not specified.
     * @param sizeThreshold The threshold, in bytes, below which items will be
     *        retained in memory and above which they will be stored as a file.
     * @param repository The data repository, which is the directory in which
     *        files will be created, should the item size exceed the threshold.
     */
    UploadRequestFileItem(final String fieldName, final String contentType, final boolean isFormField,
            final String fileName, final int sizeThreshold, final File repository) {
        this.fieldName = fieldName;
        this.contentType = contentType;
        this.isFormField = isFormField;
        this.fileName = fileName;
        this.sizeThreshold = sizeThreshold;
        this.repository = repository;
    }

    /**
     * <p>returns an {@link java.io.InputStream InputStream}that can be used to
     * retrieve the contents of the file.</p>
     * 
     * @return An {@link java.io.InputStream InputStream}that can be used to
     *         retrieve the contents of the file.
     * @exception IOException if an error occurs.
     */
    public InputStream getInputStream() throws IOException {
        InputStream result = null;
        if (!dfos.isInMemory()) {
            result = new FileInputStream(dfos.getFile());
        } else {
            if (cachedContent == null) {
                cachedContent = dfos.getData();
            }
            result = new ByteArrayInputStream(cachedContent);
        }
        return result;
    }

    /**
     * <p>returns the content type passed by the browser or <code>null</code> if
     * not defined.</p>
     * 
     * @return The content type passed by the browser or <code>null</code> if
     *         not defined.
     */
    public String getContentType() {
        return contentType;
    }

    /**
     * <p>returns the original filename in the client's filesystem.</p>
     * 
     * @return The original filename in the client's filesystem.
     */
    public String getName() {
        return fileName;
    }

    /**
     * <p>provides a hint as to whether or not the file contents will be read from
     * memory.</p>
     * 
     * @return <code>true</code> if the file contents will be read from memory;
     *         <code>false</code> otherwise.
     */
    public boolean isInMemory() {
        return dfos.isInMemory();
    }

    /**
     * <p>returns the size of the file.</p>
     * 
     * @return The size of the file, in bytes.
     */
    public long getSize() {
        long result = 0;
        if (cachedContent != null) {
            result = cachedContent.length;
        } else if (dfos.isInMemory()) {
            result = dfos.getData().length;
        } else {
            result = dfos.getFile().length();
        }
        return result;
    }

    /**
     * <p>returns the contents of the file as an array of bytes. If the contents 
     * of the file were not yet cached in memory, they will be loaded from the 
     * disk storage and cached.</p>
     * 
     * @return The contents of the file as an array of bytes.
     */
    public byte[] get() {
        byte[] result = null;
        if (dfos.isInMemory()) {
            if (cachedContent == null) {
                cachedContent = dfos.getData();
            }
            result = cachedContent;
        } else {
            byte[] fileData = new byte[(int) getSize()];
            FileInputStream fis = null;
            try {
                fis = new FileInputStream(dfos.getFile());
                fis.read(fileData);
            } catch (IOException e) {
                fileData = null;
            } finally {
                if (fis != null) {
                    try {
                        fis.close();
                    } catch (IOException e) {
                        // ignore
                    }
                }
            }
            result = fileData;
        }
        return result;
    }

    /**
     * <p>returns the contents of the file as a String, using the specified 
     * encoding. This method uses {@link #get()}to retrieve the contents of the 
     * file.</p>
     * 
     * @param encoding The character encoding to use.
     * @return The contents of the file, as a string.
     * @exception UnsupportedEncodingException if the requested character encoding
     *            is not available.
     */
    public String getString(final String encoding) throws UnsupportedEncodingException {
        return new String(get(), encoding);
    }

    /**
     * <p>returns the contents of the file as a String, using the default 
     * character encoding. This method uses {@link #get()}to retrieve the contents
     * of the file.</p>
     * 
     * @return The contents of the file, as a string.
     */
    public String getString() {
        return new String(get());
    }

    /**
     * <p>a convenience method to write an uploaded item to disk. The client code 
     * is not concerned with whether or not the item is stored in memory, or on 
     * disk in a temporary location. They just want to write the uploaded item to
     * a file.</p>
     * 
     * <p>This implementation first attempts to rename the uploaded item to the
     * specified destination file, if the item was originally written to disk.
     * Otherwise, the data will be copied to the specified file.</p>
     * 
     * <p>This method is only guaranteed to work <em>once</em>, the first time it
     * is invoked for a particular item. This is because, in the event that the
     * method renames a temporary file, that file will no longer be available to
     * copy or rename again at a later time.</p>
     * 
     * @param file The <code>File</code> into which the uploaded item should be
     *          stored.
     * @exception Exception if an error occurs.
     */
    public void write(final File file) throws IOException {
        if (isInMemory()) {
            FileOutputStream fout = null;
            try {
                fout = new FileOutputStream(file);
                fout.write(get());
            } finally {
                if (fout != null) {
                    fout.close();
                }
            }
        } else {
            File outputFile = getStoreLocation();
            if (outputFile != null) {
                /*
                 * The uploaded file is being stored on disk in a temporary location so
                 * move it to the desired file.
                 */
                if (!outputFile.renameTo(file)) {
                    BufferedInputStream in = null;
                    BufferedOutputStream out = null;
                    try {
                        in = new BufferedInputStream(new FileInputStream(outputFile));
                        out = new BufferedOutputStream(new FileOutputStream(file));
                        byte[] bytes = new byte[2048];
                        int s = 0;
                        while ((s = in.read(bytes)) != -1) {
                            out.write(bytes, 0, s);
                        }
                    } finally {
                        try {
                            if (in != null) {
                                in.close();
                            }
                        } catch (IOException e) {
                            // ignore
                        }
                        try {
                            if (out != null) {
                                out.close();
                            }
                        } catch (IOException e) {
                            // ignore
                        }
                    }
                }
            } else {
                /*
                 * For whatever reason we cannot write the file to disk.
                 */
                throw new IOException("Cannot write uploaded file to disk!");
            }
        }
    }

    /**
     * <p>deletes the underlying storage for a file item, including deleting any
     * associated temporary disk file. Although this storage will be deleted
     * automatically when the <code>FileItem</code> instance is garbage
     * collected, this method can be used to ensure that this is done at an
     * earlier time, thus preserving system resources.</p>
     */
    public void delete() {
        cachedContent = null;
        File outputFile = getStoreLocation();
        if (outputFile != null && outputFile.exists()) {
            outputFile.delete();
        }
    }

    /**
     * <p>returns the name of the field in the multipart form corresponding to
     * this file item.</p>
     * 
     * @return The name of the form field.
     * @see #setFieldName(java.lang.String)
     */
    public String getFieldName() {
        return fieldName;
    }

    /**
     * <p>sets the field name used to reference this file item.</p>
     * 
     * @param fieldName The name of the form field.
     * @see #getFieldName()
     */
    public void setFieldName(final String fieldName) {
        this.fieldName = fieldName;
    }

    /**
     * <p>determines whether or not a <code>FileItem</code> instance represents a
     * simple form field.</p>
     * 
     * @return <code>true</code> if the instance represents a simple form field;
     *         <code>false</code> if it represents an uploaded file.
     * @see #setFormField(boolean)
     */
    public boolean isFormField() {
        return isFormField;
    }

    /**
     * <p>specifies whether or not a <code>FileItem</code> instance represents a
     * simple form field.</p>
     * 
     * @param isFormField <code>true</code> if the instance represents a simple 
     *        form field; <code>false</code> if it represents an uploaded file.
     * @see #isFormField()
     */
    public void setFormField(final boolean isFormField) {
        this.isFormField = isFormField;
    }

    /**
     * <p>returns an {@link java.io.OutputStream OutputStream}that can be used for
     * storing the contents of the file.</p>
     * 
     * @return An {@link java.io.OutputStream OutputStream}that can be used for
     *         storing the contensts of the file.
     * @exception IOException if an error occurs.
     */
    public OutputStream getOutputStream() throws IOException {
        if (dfos == null) {
            File outputFile = getTempFile();
            if (isFormField()) {
                dfos = new DeferredFileOutputStream(DEFAULT_OUTPUTSTREAM_SIZE, outputFile);
            } else {
                dfos = new DeferredFileOutputStream(sizeThreshold, outputFile);
            }
        }
        return dfos;
    }

    /**
     * <p>returns the {@link java.io.File}object for the <code>FileItem</code>'s
     * data's temporary location on the disk. Note that for <code>FileItem</code>
     * s that have their data stored in memory, this method will return
     * <code>null</code>. When handling large files, you can use
     * {@link java.io.File#renameTo(java.io.File)}to move the file to new
     * location without copying the data, if the source and destination locations
     * reside within the same logical volume.</p>
     * 
     * @return The data file, or <code>null</code> if the data is stored in
     *         memory.
     */
    public File getStoreLocation() {
        return dfos.getFile();
    }

    /**
     * <p>removes the file contents from the temporary storage.</p>
     */
    protected void finalize() {
        File outputFile = dfos.getFile();
        if (outputFile != null && outputFile.exists()) {
            outputFile.delete();
        }
    }

    /**
     * <p>creates and returns a {@link java.io.File File}representing a uniquely
     * named temporary file in the configured repository path.</p>
     * 
     * @return The {@link java.io.File File}to be used for temporary storage.
     */
    protected File getTempFile() {
        File tempDir = repository;
        if (tempDir == null) {
            tempDir = new File(System.getProperty("java.io.tmpdir"));
        }
        String fileName = "upload_" + getUniqueId() + ".tmp";
        File result = new File(tempDir, fileName);
        result.deleteOnExit();
        return result;
    }

    /**
     * <p>returns an identifier that is unique within the class loader used to
     * load this class, but does not have random-like apearance.</p>
     * 
     * @return A String with the non-random looking instance identifier.
     */
    private static String getUniqueId() {
        int current;
        synchronized (UploadRequestFileItem.class) {
            current = counter++;
        }
        String result = Integer.toString(current);
        // If you manage to get more than 100 million of ids, you'll
        // start getting ids longer than 8 characters.
        if (current < 100000000) {
            result = ("00000000" + result).substring(result.length());
        }
        return result;
    }
}