Java tutorial
/******************************************************************************* * 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; } }