View Javadoc
1   /*
2    * Copyright 2002-2015 the original author or authors.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *      http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  
17  package org.springframework.web.multipart.commons;
18  
19  import java.io.ByteArrayInputStream;
20  import java.io.File;
21  import java.io.IOException;
22  import java.io.InputStream;
23  import java.io.Serializable;
24  
25  import org.apache.commons.fileupload.FileItem;
26  import org.apache.commons.fileupload.FileUploadException;
27  import org.apache.commons.fileupload.disk.DiskFileItem;
28  import org.apache.commons.logging.Log;
29  import org.apache.commons.logging.LogFactory;
30  
31  import org.springframework.web.multipart.MultipartFile;
32  
33  /**
34   * MultipartFile implementation for Apache Commons FileUpload.
35   *
36   * @author Trevor D. Cook
37   * @author Juergen Hoeller
38   * @since 29.09.2003
39   * @see CommonsMultipartResolver
40   */
41  @SuppressWarnings("serial")
42  public class CommonsMultipartFile implements MultipartFile, Serializable {
43  
44  	protected static final Log logger = LogFactory.getLog(CommonsMultipartFile.class);
45  
46  	private final FileItem fileItem;
47  
48  	private final long size;
49  
50  
51  	/**
52  	 * Create an instance wrapping the given FileItem.
53  	 * @param fileItem the FileItem to wrap
54  	 */
55  	public CommonsMultipartFile(FileItem fileItem) {
56  		this.fileItem = fileItem;
57  		this.size = this.fileItem.getSize();
58  	}
59  
60  	/**
61  	 * Return the underlying {@code org.apache.commons.fileupload.FileItem}
62  	 * instance. There is hardly any need to access this.
63  	 */
64  	public final FileItem getFileItem() {
65  		return this.fileItem;
66  	}
67  
68  
69  	@Override
70  	public String getName() {
71  		return this.fileItem.getFieldName();
72  	}
73  
74  	@Override
75  	public String getOriginalFilename() {
76  		String filename = this.fileItem.getName();
77  		if (filename == null) {
78  			// Should never happen.
79  			return "";
80  		}
81  		// check for Unix-style path
82  		int pos = filename.lastIndexOf("/");
83  		if (pos == -1) {
84  			// check for Windows-style path
85  			pos = filename.lastIndexOf("\\");
86  		}
87  		if (pos != -1)  {
88  			// any sort of path separator found
89  			return filename.substring(pos + 1);
90  		}
91  		else {
92  			// plain name
93  			return filename;
94  		}
95  	}
96  
97  	@Override
98  	public String getContentType() {
99  		return this.fileItem.getContentType();
100 	}
101 
102 	@Override
103 	public boolean isEmpty() {
104 		return (this.size == 0);
105 	}
106 
107 	@Override
108 	public long getSize() {
109 		return this.size;
110 	}
111 
112 	@Override
113 	public byte[] getBytes() {
114 		if (!isAvailable()) {
115 			throw new IllegalStateException("File has been moved - cannot be read again");
116 		}
117 		byte[] bytes = this.fileItem.get();
118 		return (bytes != null ? bytes : new byte[0]);
119 	}
120 
121 	@Override
122 	public InputStream getInputStream() throws IOException {
123 		if (!isAvailable()) {
124 			throw new IllegalStateException("File has been moved - cannot be read again");
125 		}
126 		InputStream inputStream = this.fileItem.getInputStream();
127 		return (inputStream != null ? inputStream : new ByteArrayInputStream(new byte[0]));
128 	}
129 
130 	@Override
131 	public void transferTo(File dest) throws IOException, IllegalStateException {
132 		if (!isAvailable()) {
133 			throw new IllegalStateException("File has already been moved - cannot be transferred again");
134 		}
135 
136 		if (dest.exists() && !dest.delete()) {
137 			throw new IOException(
138 					"Destination file [" + dest.getAbsolutePath() + "] already exists and could not be deleted");
139 		}
140 
141 		try {
142 			this.fileItem.write(dest);
143 			if (logger.isDebugEnabled()) {
144 				String action = "transferred";
145 				if (!this.fileItem.isInMemory()) {
146 					action = isAvailable() ? "copied" : "moved";
147 				}
148 				logger.debug("Multipart file '" + getName() + "' with original filename [" +
149 						getOriginalFilename() + "], stored " + getStorageDescription() + ": " +
150 						action + " to [" + dest.getAbsolutePath() + "]");
151 			}
152 		}
153 		catch (FileUploadException ex) {
154 			throw new IllegalStateException(ex.getMessage());
155 		}
156 		catch (IOException ex) {
157 			throw ex;
158 		}
159 		catch (Exception ex) {
160 			logger.error("Could not transfer to file", ex);
161 			throw new IOException("Could not transfer to file: " + ex.getMessage());
162 		}
163 	}
164 
165 	/**
166 	 * Determine whether the multipart content is still available.
167 	 * If a temporary file has been moved, the content is no longer available.
168 	 */
169 	protected boolean isAvailable() {
170 		// If in memory, it's available.
171 		if (this.fileItem.isInMemory()) {
172 			return true;
173 		}
174 		// Check actual existence of temporary file.
175 		if (this.fileItem instanceof DiskFileItem) {
176 			return ((DiskFileItem) this.fileItem).getStoreLocation().exists();
177 		}
178 		// Check whether current file size is different than original one.
179 		return (this.fileItem.getSize() == this.size);
180 	}
181 
182 	/**
183 	 * Return a description for the storage location of the multipart content.
184 	 * Tries to be as specific as possible: mentions the file location in case
185 	 * of a temporary file.
186 	 */
187 	public String getStorageDescription() {
188 		if (this.fileItem.isInMemory()) {
189 			return "in memory";
190 		}
191 		else if (this.fileItem instanceof DiskFileItem) {
192 			return "at [" + ((DiskFileItem) this.fileItem).getStoreLocation().getAbsolutePath() + "]";
193 		}
194 		else {
195 			return "on disk";
196 		}
197 	}
198 
199 }