001: // Copyright (C) 1999-2001 by Jason Hunter <jhunter_AT_acm_DOT_org>.
002: // All rights reserved. Use of this class is limited.
003: // Please see the LICENSE for more information.
004:
005: package com.oreilly.servlet.multipart;
006:
007: import java.io.File;
008: import java.io.InputStream;
009: import java.io.OutputStream;
010: import java.io.BufferedOutputStream;
011: import java.io.FileOutputStream;
012: import java.io.IOException;
013: import javax.servlet.ServletInputStream;
014:
015: /**
016: * A <code>FilePart</code> is an upload part which represents a
017: * <code>INPUT TYPE="file"</code> form parameter. Note that because file
018: * upload data arrives via a single InputStream, each FilePart's contents
019: * must be read before moving onto the next part. Don't try to store a
020: * FilePart object for later processing because by then their content will
021: * have been passed by.
022: *
023: * @author Geoff Soutter
024: * @version 1.2, 2001/01/22, getFilePath() addition thanks to Stefan Eissing
025: * @version 1.1, 2000/11/26, writeTo() bug fix thanks to Mike Shivas
026: * @version 1.0, 2000/10/27, initial revision
027: */
028: public class FilePart extends Part {
029:
030: /** "file system" name of the file */
031: private String fileName;
032:
033: /** path of the file as sent in the request, if given */
034: private String filePath;
035:
036: /** content type of the file */
037: private String contentType;
038:
039: /** input stream containing file data */
040: private PartInputStream partInput;
041:
042: /** file rename policy */
043: private FileRenamePolicy policy;
044:
045: /**
046: * Construct a file part; this is called by the parser.
047: *
048: * @param name the name of the parameter.
049: * @param in the servlet input stream to read the file from.
050: * @param boundary the MIME boundary that delimits the end of file.
051: * @param contentType the content type of the file provided in the
052: * MIME header.
053: * @param fileName the file system name of the file provided in the
054: * MIME header.
055: * @param filePath the file system path of the file provided in the
056: * MIME header (as specified in disposition info).
057: *
058: * @exception IOException if an input or output exception has occurred.
059: */
060: FilePart(String name, ServletInputStream in, String boundary,
061: String contentType, String fileName, String filePath)
062: throws IOException {
063: super (name);
064: this .fileName = fileName;
065: this .filePath = filePath;
066: this .contentType = contentType;
067: partInput = new PartInputStream(in, boundary);
068: }
069:
070: /**
071: * Puts in place the specified policy for handling file name collisions.
072: */
073: public void setRenamePolicy(FileRenamePolicy policy) {
074: this .policy = policy;
075: }
076:
077: /**
078: * Returns the name that the file was stored with on the remote system,
079: * or <code>null</code> if the user didn't enter a file to be uploaded.
080: * Note: this is not the same as the name of the form parameter used to
081: * transmit the file; that is available from the <code>getName</code>
082: * method. Further note: if file rename logic is in effect, the file
083: * name can change during the writeTo() method when there's a collision
084: * with another file of the same name in the same directory. If this
085: * matters to you, be sure to pay attention to when you call the method.
086: *
087: * @return name of file uploaded or <code>null</code>.
088: *
089: * @see Part#getName()
090: */
091: public String getFileName() {
092: return fileName;
093: }
094:
095: /**
096: * Returns the full path and name of the file on the remote system,
097: * or <code>null</code> if the user didn't enter a file to be uploaded.
098: * If path information was not supplied by the remote system, this method
099: * will return the same as <code>getFileName()</code>.
100: *
101: * @return path of file uploaded or <code>null</code>.
102: *
103: * @see Part#getName()
104: */
105: public String getFilePath() {
106: return filePath;
107: }
108:
109: /**
110: * Returns the content type of the file data contained within.
111: *
112: * @return content type of the file data.
113: */
114: public String getContentType() {
115: return contentType;
116: }
117:
118: /**
119: * Returns an input stream which contains the contents of the
120: * file supplied. If the user didn't enter a file to upload
121: * there will be <code>0</code> bytes in the input stream.
122: * It's important to read the contents of the InputStream
123: * immediately and in full before proceeding to process the
124: * next part. The contents will otherwise be lost on moving
125: * to the next part.
126: *
127: * @return an input stream containing contents of file.
128: */
129: public InputStream getInputStream() {
130: return partInput;
131: }
132:
133: /**
134: * Write this file part to a file or directory. If the user
135: * supplied a file, we write it to that file, and if they supplied
136: * a directory, we write it to that directory with the filename
137: * that accompanied it. If this part doesn't contain a file this
138: * method does nothing.
139: *
140: * @return number of bytes written
141: * @exception IOException if an input or output exception has occurred.
142: */
143: public long writeTo(File fileOrDirectory) throws IOException {
144: long written = 0;
145:
146: OutputStream fileOut = null;
147: try {
148: // Only do something if this part contains a file
149: if (fileName != null) {
150: // Check if user supplied directory
151: File file;
152: if (fileOrDirectory.isDirectory()) {
153: // Write it to that dir the user supplied,
154: // with the filename it arrived with
155: file = new File(fileOrDirectory, fileName);
156: } else {
157: // Write it to the file the user supplied,
158: // ignoring the filename it arrived with
159: file = fileOrDirectory;
160: }
161: if (policy != null) {
162: file = policy.rename(file);
163: fileName = file.getName();
164: }
165: fileOut = new BufferedOutputStream(
166: new FileOutputStream(file));
167: written = write(fileOut);
168: }
169: } finally {
170: if (fileOut != null)
171: fileOut.close();
172: }
173: return written;
174: }
175:
176: /**
177: * Write this file part to the given output stream. If this part doesn't
178: * contain a file this method does nothing.
179: *
180: * @return number of bytes written.
181: * @exception IOException if an input or output exception has occurred.
182: */
183: public long writeTo(OutputStream out) throws IOException {
184: long size = 0;
185: // Only do something if this part contains a file
186: if (fileName != null) {
187: // Write it out
188: size = write(out);
189: }
190: return size;
191: }
192:
193: /**
194: * Internal method to write this file part; doesn't check to see
195: * if it has contents first.
196: *
197: * @return number of bytes written.
198: * @exception IOException if an input or output exception has occurred.
199: */
200: long write(OutputStream out) throws IOException {
201: // decode macbinary if this was sent
202: if (contentType.equals("application/x-macbinary")) {
203: out = new MacBinaryDecoderOutputStream(out);
204: }
205: long size = 0;
206: int read;
207: byte[] buf = new byte[8 * 1024];
208: while ((read = partInput.read(buf)) != -1) {
209: out.write(buf, 0, read);
210: size += read;
211: }
212: return size;
213: }
214:
215: /**
216: * Returns <code>true</code> to indicate this part is a file.
217: *
218: * @return true.
219: */
220: public boolean isFile() {
221: return true;
222: }
223: }
|