Source code: com/jcorporate/expresso/core/misc/upload/FileItem.java
1 /* ====================================================================
2 * The Jcorporate Apache Style Software License, Version 1.2 05-07-2002
3 *
4 * Copyright (c) 1995-2002 Jcorporate Ltd. All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 *
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 *
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in
15 * the documentation and/or other materials provided with the
16 * distribution.
17 *
18 * 3. The end-user documentation included with the redistribution,
19 * if any, must include the following acknowledgment:
20 * "This product includes software developed by Jcorporate Ltd.
21 * (http://www.jcorporate.com/)."
22 * Alternately, this acknowledgment may appear in the software itself,
23 * if and wherever such third-party acknowledgments normally appear.
24 *
25 * 4. "Jcorporate" and product names such as "Expresso" must
26 * not be used to endorse or promote products derived from this
27 * software without prior written permission. For written permission,
28 * please contact info@jcorporate.com.
29 *
30 * 5. Products derived from this software may not be called "Expresso",
31 * or other Jcorporate product names; nor may "Expresso" or other
32 * Jcorporate product names appear in their name, without prior
33 * written permission of Jcorporate Ltd.
34 *
35 * 6. No product derived from this software may compete in the same
36 * market space, i.e. framework, without prior written permission
37 * of Jcorporate Ltd. For written permission, please contact
38 * partners@jcorporate.com.
39 *
40 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
41 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
42 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
43 * DISCLAIMED. IN NO EVENT SHALL JCORPORATE LTD OR ITS CONTRIBUTORS
44 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
45 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
46 * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
47 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
48 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
49 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
50 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
51 * SUCH DAMAGE.
52 * ====================================================================
53 *
54 * This software consists of voluntary contributions made by many
55 * individuals on behalf of the Jcorporate Ltd. Contributions back
56 * to the project(s) are encouraged when you make modifications.
57 * Please send them to support@jcorporate.com. For more information
58 * on Jcorporate Ltd. and its products, please see
59 * <http://www.jcorporate.com/>.
60 *
61 * Portions of this software are based upon other open source
62 * products and are subject to their respective licenses.
63 */
64
65 package com.jcorporate.expresso.core.misc.upload;
66
67 import com.jcorporate.expresso.core.misc.StringUtil;
68 import org.apache.struts.upload.FormFile;
69
70 import javax.activation.DataSource;
71 import java.io.ByteArrayInputStream;
72 import java.io.ByteArrayOutputStream;
73 import java.io.File;
74 import java.io.FileInputStream;
75 import java.io.FileNotFoundException;
76 import java.io.FileOutputStream;
77 import java.io.IOException;
78 import java.io.InputStream;
79 import java.io.OutputStream;
80 import java.io.UnsupportedEncodingException;
81
82
83 /**
84 * <p> This class represents a file that was received by Turbine using
85 * <code>multipart/form-data</code> POST request.
86 * <p/>
87 * <p> After retrieving an instance of this class from the {@link
88 * org.apache.turbine.util.ParameterParser ParameterParser} (see
89 * {@link org.apache.turbine.util.ParameterParser#getFileItem(String)
90 * ParameterParser.getFileItem(String)} and {@link
91 * org.apache.turbine.util.ParameterParser#getFileItems(String)
92 * ParameterParser.getFileItems(String)}) you can use it to acces the
93 * data that was sent by the browser. You may either request all
94 * contents of file at once using {@link #get()} or request an {@link
95 * java.io.InputStream InputStream} with {@link #getStream()} and
96 * process the file without attempting to load it into memory, which
97 * may come handy with large files.
98 *
99 * @author <a href="mailto:Rafal.Krzewski@e-point.pl">Rafal Krzewski</a>
100 * @version $Id: FileItem.java,v 1.8 2004/11/17 20:48:13 lhamel Exp $
101 */
102 public class FileItem
103 implements FormFile, DataSource {
104
105 /**
106 * The maximal size of request that will have it's elements stored
107 * in memory.
108 */
109 public static final int DEFAULT_UPLOAD_SIZE_THRESHOLD = 10240;
110
111 /**
112 * The original filename in the user's filesystem.
113 */
114 protected String fileName;
115
116 /**
117 * The content type passed by the browser or <code>null</code> if
118 * not defined.
119 */
120 protected String contentType;
121
122 /**
123 * Cached contents of the file.
124 */
125 protected byte[] content;
126
127 /**
128 * Temporary storage location.
129 */
130 protected File storeLocation = null;
131
132 /**
133 * Temporary storage for in-memory files.
134 */
135 protected ByteArrayOutputStream byteStream;
136
137 /**
138 * Constructs a new <code>FileItem</code>.
139 * <p/>
140 * <p>Use {@link #newInstance(String,String,String,int)} to
141 * instantiate <code>FileItems</code>.
142 *
143 * @param fileName The original filename in the user's filesystem.
144 * @param contentType The content type passed by the browser or
145 * <code>null</code> if not defined.
146 */
147 protected FileItem(String fileName, String contentType) {
148 this.fileName = fileName;
149 this.contentType = contentType;
150 }
151
152 /**
153 * Returns the original filename in the user's filesystem.
154 *
155 * @return The original filename in the user's filesystem.
156 */
157 public String getFileName() {
158 return fileName;
159 }
160
161 /**
162 * Returns the content type passed by the browser or
163 * <code>null</code> if not defined.
164 *
165 * @return The content type passed by the browser or
166 * <code>null</code> if not defined.
167 */
168 public String getContentType() {
169 return contentType;
170 }
171
172 /**
173 * Provides a hint if the file contents will be read from memory.
174 *
175 * @return <code>True</code> if the file contents will be read
176 * from memory.
177 */
178 public boolean inMemory() {
179 return (content != null || byteStream != null);
180 }
181
182 /**
183 * Returns the size of the file.
184 *
185 * @return The size of the file.
186 */
187 public long getSize() {
188 if (storeLocation != null) {
189 return storeLocation.length();
190 } else if (byteStream != null) {
191 return byteStream.size();
192 } else {
193 return content.length;
194 }
195 }
196
197 /**
198 * Returns the contents of the file as an array of bytes. If the
199 * contents of the file were not yet cached int the memory, they
200 * will be loaded from the disk storage and chached.
201 *
202 * @return The contents of the file as an array of bytes.
203 */
204 public byte[] get() {
205 if (content == null) {
206 if (storeLocation != null) {
207 content = new byte[(int) getSize()];
208
209 try {
210 FileInputStream fis = new FileInputStream(storeLocation);
211 fis.read(content);
212 } catch (Exception e) {
213 content = null;
214 }
215 } else {
216 content = byteStream.toByteArray();
217 byteStream = null;
218 }
219 }
220
221 return content;
222 }
223
224 /**
225 * Returns the contents of the file as a String, using default
226 * encoding. This method uses {@link #get()} to retrieve the
227 * contents of the file.
228 *
229 * @return The contents of the file.
230 */
231 public String getString() {
232 return new String(get());
233 }
234
235 /**
236 * Returns the contents of the file as a String, using specified
237 * encoding. This method uses {@link #get()} to retireve the
238 * contents of the file.<br>
239 *
240 * @param encoding The encoding to use.
241 * @return The contents of the file.
242 */
243 public String getString(String encoding)
244 throws UnsupportedEncodingException {
245 return new String(get(), encoding);
246 }
247
248 /**
249 * Returns an {@link java.io.InputStream InputStream} that can be
250 * used to retrieve the contents of the file.
251 *
252 * @return An {@link java.io.InputStream InputStream} that can be
253 * used to retrieve the contents of the file.
254 * @throws Exception A generic exception.
255 */
256 public InputStream getStream()
257 throws Exception {
258 if (content == null) {
259 if (storeLocation != null) {
260 try {
261 return new FileInputStream(storeLocation);
262 } catch (FileNotFoundException e) {
263 throw new Exception("FileItem: stored item was lost");
264 }
265 } else {
266 content = byteStream.toByteArray();
267 byteStream = null;
268 }
269 }
270
271 return new ByteArrayInputStream(content);
272 }
273
274 /**
275 * Returns the {@link java.io.File} objects for the FileItems's
276 * data temporary location on the disk. Note that for
277 * <code>FileItems</code> that have their data stored in memory
278 * this method will return <code>null</code>. When handling large
279 * files, you can use {@link java.io.File#renameTo(File)} to
280 * move the file to new location without copying the data, if the
281 * source and destination locations reside within the same logical
282 * volume.
283 *
284 * @return A File.
285 */
286 public File getStoreLocation() {
287 return storeLocation;
288 }
289
290 /**
291 * Returns an {@link java.io.OutputStream OutputStream} that can
292 * be used for storing the contensts of the file.
293 *
294 * @return an {@link java.io.OutputStream OutputStream} that can be
295 * used for storing the contensts of the file.
296 */
297 public OutputStream getOutputStream()
298 throws IOException {
299 if (storeLocation == null) {
300 return byteStream;
301 } else {
302 return new FileOutputStream(storeLocation);
303 }
304 }
305
306 /**
307 * Instantiates a FileItem. It uses <code>requestSize</code> to
308 * decide what temporary storage approach the new item should
309 * take. The largest request that will have its items cached in
310 * memory can be configured in
311 * <code>TurbineResources.properties</code> in the entry named
312 * <code>file.upload.size.threshold</code>
313 *
314 * @param path A String.
315 * @param name The original filename in the user's filesystem.
316 * @param contentType The content type passed by the browser or
317 * <code>null</code> if not defined.
318 * @param requestSize The total size of the POST request this item
319 * belongs to.
320 * @param storeAsFile Set to true if you want it stored as a local file.
321 * @return A FileItem.
322 */
323 public static FileItem newInstance(String path, String name,
324 String contentType, int requestSize,
325 boolean storeAsFile) {
326 FileItem item = new FileItem(name, contentType);
327
328 if (storeAsFile) {
329
330 /* if we need to stash it in a file */
331 try {
332
333 // NextNumber myNext = new NextNumber();
334 // myNext.setDBName("default");
335 // Temporary filename becomes <path>/upload<seq-num>-<orig name>
336 name = StringUtil.replace(name, "\\", "/");
337
338 File tmpFile = new File(name);
339
340 // String fileName = path + "upload" + myNext.getNext("UPFILE")
341 // + "-" + tmpFile.getName();
342 File tempDir = new File(path);
343 tempDir.mkdirs();
344
345 //New method use SDK's version to create a temp file with
346 //the appropriate prefix and suffix
347 item.storeLocation = File.createTempFile("upload",
348 "-" +
349 tmpFile.getName(),
350 tempDir);
351
352 // item.storeLocation = new File(fileName);
353 } catch (IOException de) {
354 throw new IllegalArgumentException("Unable to create upload temp file " +
355 de.getMessage());
356 }
357 } else {
358 item.byteStream = new ByteArrayOutputStream();
359 }
360
361 return item;
362 }
363 /** Methods required by the Struts FormFile interface */
364 /**
365 * Set the content type for this file
366 *
367 * @param contentType The content type
368 */
369 public void setContentType(String contentType) {
370 this.contentType = contentType;
371 }
372
373 /**
374 * Get the size of this file
375 *
376 * @return An int representing the size of the file in bytes
377 */
378 public int getFileSize() {
379 return new Long(getSize()).intValue();
380 }
381
382 /**
383 * Set the file size
384 *
385 * @param filesize An int reprsenting the size of the file in bytes
386 */
387 public void setFileSize(int filesize) {
388
389 /* Ignore this */
390 }
391
392 /**
393 * Set the filename of this file
394 *
395 * @param newFileName The name of the file
396 */
397 public void setFileName(String newFileName) {
398 fileName = newFileName;
399 }
400
401 /**
402 * Get the data in byte array for for this file. Note that this can be
403 * a very hazardous method, files can be large enough to cause
404 * OutOfMemoryErrors. Short of being deprecated, it's strongly recommended
405 * that you use {@link #getInputStream() getInputStream} to get the file
406 * data.
407 *
408 * @return An array of bytes representing the data contained
409 * in the form file
410 * @throws FileNotFoundException If some sort of file representation
411 * cannot be found for the FormFile
412 * @throws IOException If there is some sort of IOException
413 */
414 public byte[] getFileData()
415 throws FileNotFoundException, IOException {
416 byte[] b = null;
417 getInputStream().read(b);
418
419 return b;
420 }
421
422 /**
423 * Get an InputStream that represents this file. This is the preferred
424 * method of getting file data.
425 *
426 * @return an InputStream object.
427 * @throws FileNotFoundException If some sort of file representation
428 * cannot be found for the FormFile
429 * @throws IOException If there is some sort of IOException
430 */
431 public InputStream getInputStream()
432 throws FileNotFoundException, IOException {
433 return new FileInputStream(getFileName());
434 }
435
436 /**
437 * Destroy all content for this form file.
438 * Implementations should remove any temporary
439 * files or any temporary file data stored somewhere
440 */
441 public void destroy() {
442 File theFile = new File(getFileName());
443 theFile.delete();
444 }
445
446 /**
447 * Returns the name of the FileItem
448 *
449 * @return java.lang.String
450 */
451 public String getName() {
452 return this.fileName;
453 }
454 }