001: // Copyright (c) 2003-2007, Jodd Team (jodd.sf.net). All Rights Reserved.
002:
003: package jodd.servlet.upload.impl;
004:
005: import jodd.io.FastByteArrayOutputStream;
006: import jodd.io.FileUtil;
007: import jodd.io.FileNameUtil;
008: import jodd.util.SystemUtil;
009: import jodd.servlet.upload.FileUpload;
010: import jodd.servlet.upload.MultipartRequestInputStream;
011: import jodd.servlet.upload.FileUploadFactory;
012:
013: import java.io.File;
014: import java.io.IOException;
015: import java.io.FileOutputStream;
016: import java.io.BufferedOutputStream;
017:
018: /**
019: * Smart {@link FileUpload} implementation that defer the action of what to do with uploaded file
020: * for later. Internally, it stores uploaded file either in memory if it is small, or, in all
021: * other cases, it stores them in TEMP folder.
022: */
023: public class AdaptiveFileUpload extends FileUpload {
024:
025: public AdaptiveFileUpload(MultipartRequestInputStream input) {
026: super (input);
027: }
028:
029: public AdaptiveFileUpload(MultipartRequestInputStream input,
030: int memoryThreshold, File uploadPath, int maxFileSize,
031: boolean breakOnError, String[] extensions, boolean allowed) {
032: super (input);
033: this .memoryThreshold = memoryThreshold;
034: this .uploadPath = uploadPath;
035: this .maxFileSize = maxFileSize;
036: this .breakOnError = breakOnError;
037: this .fileExtensions = extensions;
038: this .allowFileExtensions = allowed;
039: }
040:
041: // ---------------------------------------------------------------- settings
042:
043: protected int memoryThreshold;
044: protected File uploadPath;
045: protected int maxFileSize;
046: protected boolean breakOnError;
047: protected String[] fileExtensions;
048: public boolean allowFileExtensions;
049:
050: public int getMemoryThreshold() {
051: return memoryThreshold;
052: }
053:
054: public File getUploadPath() {
055: return uploadPath;
056: }
057:
058: public int getMaxFileSize() {
059: return maxFileSize;
060: }
061:
062: public boolean isBreakOnError() {
063: return breakOnError;
064: }
065:
066: public String[] getFileExtensions() {
067: return fileExtensions;
068: }
069:
070: public boolean isAllowFileExtensions() {
071: return allowFileExtensions;
072: }
073:
074: // ---------------------------------------------------------------- properties
075:
076: protected File tempFile;
077: protected byte[] data;
078: protected boolean valid;
079:
080: /**
081: * Returns <code>true</code> if file is valid and passes all the rules.
082: */
083: public boolean isValid() {
084: return valid;
085: }
086:
087: /**
088: * Returns <code>true</code> if file upload resides in memory.
089: */
090: public boolean isInMemory() {
091: return data != null;
092: }
093:
094: // ---------------------------------------------------------------- process
095:
096: protected boolean matchFileExtension() throws IOException {
097: String fileNameExtension = FileNameUtil
098: .getExtension(getHeader().getFileName());
099: for (String fileExtension : fileExtensions) {
100: if (fileNameExtension.equalsIgnoreCase(fileExtension) == true) {
101: if (allowFileExtensions == false) { // extension matched and it is not allowed
102: if (breakOnError == true) {
103: throw new IOException(
104: "Filename extension of uploaded file in not allowed ("
105: + fileNameExtension + ").");
106: }
107: size = input.skipToBoundary();
108: return false;
109: } else {
110: return true; // extension matched and it is allowed.
111: }
112: }
113: }
114: if (allowFileExtensions == true) { // extension is not one of the allowed ones.
115: if (breakOnError == true) {
116: throw new IOException(
117: "Filename extension of uploaded file in not in the list of allowed ones ("
118: + fileNameExtension + ").");
119: }
120: size = input.skipToBoundary();
121: return false;
122: }
123: return true;
124: }
125:
126: /**
127: * Determines if upload is allowed.
128: */
129: protected boolean checkUpload() throws IOException {
130: if (fileExtensions != null) {
131: if (matchFileExtension() == false) {
132: return false;
133: }
134: }
135: return true;
136: }
137:
138: @Override
139: protected void processStream() throws IOException {
140: if (checkUpload() == false) {
141: return;
142: }
143: size = 0;
144: if (memoryThreshold > 0) {
145: FastByteArrayOutputStream fbaos = new FastByteArrayOutputStream(
146: memoryThreshold + 1);
147: int written = input.copyMax(fbaos, memoryThreshold + 1);
148: data = fbaos.toByteArray();
149: if (written <= memoryThreshold) {
150: size = data.length;
151: return;
152: }
153: }
154:
155: // create temp file and write already readed data
156: if (uploadPath == null) {
157: uploadPath = new File(SystemUtil.getTempDir());
158: }
159:
160: tempFile = File.createTempFile("upload", ".jodd", uploadPath);
161: BufferedOutputStream out = new BufferedOutputStream(
162: new FileOutputStream(tempFile));
163: if (data != null) {
164: size = data.length;
165: out.write(data);
166: data = null; // not needed anymore
167: }
168: boolean deleteTempFile = false;
169: try {
170: if (maxFileSize == -1) {
171: size += input.copyAll(out);
172: } else {
173: size += input.copyMax(out, maxFileSize - size + 1); // one more byte to detect larger files
174: if (size > maxFileSize) {
175: deleteTempFile = true;
176: valid = false;
177: if (breakOnError == true) {
178: throw new IOException("File upload ("
179: + header.getFileName() + ") too big.");
180: }
181: input.skipToBoundary();
182: return;
183: }
184: }
185: } finally {
186: try {
187: out.close();
188: } finally {
189: if (deleteTempFile) {
190: tempFile.delete();
191: tempFile = null;
192: }
193: }
194: }
195:
196: valid = true;
197: }
198:
199: // ---------------------------------------------------------------- operations
200:
201: /**
202: * Deletes file uploaded item from disk or memory.
203: */
204: public void delete() {
205: if (tempFile != null) {
206: tempFile.delete();
207: }
208: if (data != null) {
209: data = null;
210: }
211: }
212:
213: /**
214: * Writes file uploaded item.
215: */
216: public File write(String destination) throws IOException {
217: return write(new File(destination));
218: }
219:
220: /**
221: * Writes file upload item to destination folder or to destination file.
222: * Return the destination file.
223: */
224: public File write(File destination) throws IOException {
225: if (destination.isDirectory() == true) {
226: destination = new File(destination, this .header
227: .getFileName());
228: }
229: if (data != null) {
230: FileUtil.writeBytes(destination, data);
231: } else {
232: if (tempFile != null) {
233: FileUtil.move(tempFile, destination);
234: }
235: }
236: return destination;
237: }
238:
239: /**
240: * Returns the content of file upload item.
241: */
242: public byte[] getData() throws IOException {
243: if (data != null) {
244: return data;
245: }
246: if (tempFile != null) {
247: return FileUtil.readBytes(tempFile);
248: }
249: return null;
250: }
251:
252: // ---------------------------------------------------------------- factory
253:
254: public static class Factory implements FileUploadFactory {
255:
256: protected int memoryThreshold = 8192;
257: protected File uploadPath;
258: protected int maxFileSize = 102400;
259: protected boolean breakOnError;
260: protected String[] fileExtensions;
261: public boolean allowFileExtensions = true;
262:
263: public void setMemoryThreshold(int memoryThreshold) {
264: if (memoryThreshold >= 0) {
265: this .memoryThreshold = memoryThreshold;
266: }
267: }
268:
269: public void setUploadPath(File uploadPath) {
270: this .uploadPath = uploadPath;
271: }
272:
273: public void setMaxFileSize(int maxFileSize) {
274: this .maxFileSize = maxFileSize;
275: }
276:
277: public void setBreakOnError(boolean breakOnError) {
278: this .breakOnError = breakOnError;
279: }
280:
281: /**
282: * Allow or disallow set of file extensions. Only one rule can be active at time,
283: * which means user can only specify extensions that are either allowed or disallowed.
284: * Setting this value to <code>null</code> will turn this feature off.
285: */
286: public void setFileExtensions(String[] fileExtensions,
287: boolean allow) {
288: this .fileExtensions = fileExtensions;
289: this .allowFileExtensions = allow;
290: }
291:
292: public FileUpload create(MultipartRequestInputStream input) {
293: return new AdaptiveFileUpload(input, memoryThreshold,
294: uploadPath, maxFileSize, breakOnError,
295: fileExtensions, allowFileExtensions);
296: }
297: }
298:
299: }
|