1 /*
2  * Copyright 2002-2012 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.core.io;
18 
19 import java.io.File;
20 import java.io.FileInputStream;
21 import java.io.FileOutputStream;
22 import java.io.IOException;
23 import java.io.InputStream;
24 import java.io.OutputStream;
25 import java.net.URI;
26 import java.net.URL;
27 
28 import org.springframework.util.Assert;
29 import org.springframework.util.StringUtils;
30 
31 /**
32  * {@link Resource} implementation for <code>java.io.File</code> handles.
33  * Obviously supports resolution as File, and also as URL.
34  * Implements the extended {@link WritableResource} interface.
35  *
36  * @author Juergen Hoeller
37  * @since 28.12.2003
38  * @see java.io.File
39  */
40 public class FileSystemResource extends AbstractResource implements WritableResource {
41 
42 	private final File file;
43 
44 	private final String path;
45 
46 
47 	/**
48 	 * Create a new FileSystemResource from a File handle.
49 	 * <p>Note: When building relative resources via {@link #createRelative},
50 	 * the relative path will apply <i>at the same directory level</i>:
51 	 * e.g. new File("C:/dir1"), relative path "dir2" -> "C:/dir2"!
52 	 * If you prefer to have relative paths built underneath the given root
53 	 * directory, use the {@link #FileSystemResource(String) constructor with a file path}
54 	 * to append a trailing slash to the root path: "C:/dir1/", which
55 	 * indicates this directory as root for all relative paths.
56 	 * @param file a File handle
57 	 */
FileSystemResource(File file)58 	public FileSystemResource(File file) {
59 		Assert.notNull(file, "File must not be null");
60 		this.file = file;
61 		this.path = StringUtils.cleanPath(file.getPath());
62 	}
63 
64 	/**
65 	 * Create a new FileSystemResource from a file path.
66 	 * <p>Note: When building relative resources via {@link #createRelative},
67 	 * it makes a difference whether the specified resource base path here
68 	 * ends with a slash or not. In the case of "C:/dir1/", relative paths
69 	 * will be built underneath that root: e.g. relative path "dir2" ->
70 	 * "C:/dir1/dir2". In the case of "C:/dir1", relative paths will apply
71 	 * at the same directory level: relative path "dir2" -> "C:/dir2".
72 	 * @param path a file path
73 	 */
FileSystemResource(String path)74 	public FileSystemResource(String path) {
75 		Assert.notNull(path, "Path must not be null");
76 		this.file = new File(path);
77 		this.path = StringUtils.cleanPath(path);
78 	}
79 
80 	/**
81 	 * Return the file path for this resource.
82 	 */
getPath()83 	public final String getPath() {
84 		return this.path;
85 	}
86 
87 
88 	/**
89 	 * This implementation returns whether the underlying file exists.
90 	 * @see java.io.File#exists()
91 	 */
92 	@Override
exists()93 	public boolean exists() {
94 		return this.file.exists();
95 	}
96 
97 	/**
98 	 * This implementation checks whether the underlying file is marked as readable
99 	 * (and corresponds to an actual file with content, not to a directory).
100 	 * @see java.io.File#canRead()
101 	 * @see java.io.File#isDirectory()
102 	 */
103 	@Override
isReadable()104 	public boolean isReadable() {
105 		return (this.file.canRead() && !this.file.isDirectory());
106 	}
107 
108 	/**
109 	 * This implementation opens a FileInputStream for the underlying file.
110 	 * @see java.io.FileInputStream
111 	 */
getInputStream()112 	public InputStream getInputStream() throws IOException {
113 		return new FileInputStream(this.file);
114 	}
115 
116 	/**
117 	 * This implementation returns a URL for the underlying file.
118 	 * @see java.io.File#toURI()
119 	 */
120 	@Override
getURL()121 	public URL getURL() throws IOException {
122 		return this.file.toURI().toURL();
123 	}
124 
125 	/**
126 	 * This implementation returns a URI for the underlying file.
127 	 * @see java.io.File#toURI()
128 	 */
129 	@Override
getURI()130 	public URI getURI() throws IOException {
131 		return this.file.toURI();
132 	}
133 
134 	/**
135 	 * This implementation returns the underlying File reference.
136 	 */
137 	@Override
getFile()138 	public File getFile() {
139 		return this.file;
140 	}
141 
142 	/**
143 	 * This implementation returns the underlying File's length.
144 	 */
145 	@Override
contentLength()146 	public long contentLength() throws IOException {
147 		return this.file.length();
148 	}
149 
150 	/**
151 	 * This implementation creates a FileSystemResource, applying the given path
152 	 * relative to the path of the underlying file of this resource descriptor.
153 	 * @see org.springframework.util.StringUtils#applyRelativePath(String, String)
154 	 */
155 	@Override
createRelative(String relativePath)156 	public Resource createRelative(String relativePath) {
157 		String pathToUse = StringUtils.applyRelativePath(this.path, relativePath);
158 		return new FileSystemResource(pathToUse);
159 	}
160 
161 	/**
162 	 * This implementation returns the name of the file.
163 	 * @see java.io.File#getName()
164 	 */
165 	@Override
getFilename()166 	public String getFilename() {
167 		return this.file.getName();
168 	}
169 
170 	/**
171 	 * This implementation returns a description that includes the absolute
172 	 * path of the file.
173 	 * @see java.io.File#getAbsolutePath()
174 	 */
getDescription()175 	public String getDescription() {
176 		return "file [" + this.file.getAbsolutePath() + "]";
177 	}
178 
179 
180 	// implementation of WritableResource
181 
182 	/**
183 	 * This implementation checks whether the underlying file is marked as writable
184 	 * (and corresponds to an actual file with content, not to a directory).
185 	 * @see java.io.File#canWrite()
186 	 * @see java.io.File#isDirectory()
187 	 */
isWritable()188 	public boolean isWritable() {
189 		return (this.file.canWrite() && !this.file.isDirectory());
190 	}
191 
192 	/**
193 	 * This implementation opens a FileOutputStream for the underlying file.
194 	 * @see java.io.FileOutputStream
195 	 */
getOutputStream()196 	public OutputStream getOutputStream() throws IOException {
197 		return new FileOutputStream(this.file);
198 	}
199 
200 
201 	/**
202 	 * This implementation compares the underlying File references.
203 	 */
204 	@Override
equals(Object obj)205 	public boolean equals(Object obj) {
206 		return (obj == this ||
207 		    (obj instanceof FileSystemResource && this.path.equals(((FileSystemResource) obj).path)));
208 	}
209 
210 	/**
211 	 * This implementation returns the hash code of the underlying File reference.
212 	 */
213 	@Override
hashCode()214 	public int hashCode() {
215 		return this.path.hashCode();
216 	}
217 
218 }
219