001/*
002 * Copyright 2002-2016 the original author or authors.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *      https://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016
017package org.springframework.core.io;
018
019import java.io.File;
020import java.io.FileInputStream;
021import java.io.FileOutputStream;
022import java.io.IOException;
023import java.io.InputStream;
024import java.io.OutputStream;
025import java.net.URI;
026import java.net.URL;
027
028import org.springframework.util.Assert;
029import org.springframework.util.StringUtils;
030
031/**
032 * {@link Resource} implementation for {@code java.io.File} handles.
033 * Supports resolution as a {@code File} and also as a {@code URL}.
034 * Implements the extended {@link WritableResource} interface.
035 *
036 * @author Juergen Hoeller
037 * @since 28.12.2003
038 * @see java.io.File
039 */
040public class FileSystemResource extends AbstractResource implements WritableResource {
041
042        private final File file;
043
044        private final String path;
045
046
047        /**
048         * Create a new {@code FileSystemResource} from a {@link File} handle.
049         * <p>Note: When building relative resources via {@link #createRelative},
050         * the relative path will apply <i>at the same directory level</i>:
051         * e.g. new File("C:/dir1"), relative path "dir2" -> "C:/dir2"!
052         * If you prefer to have relative paths built underneath the given root
053         * directory, use the {@link #FileSystemResource(String) constructor with a file path}
054         * to append a trailing slash to the root path: "C:/dir1/", which
055         * indicates this directory as root for all relative paths.
056         * @param file a File handle
057         */
058        public FileSystemResource(File file) {
059                Assert.notNull(file, "File must not be null");
060                this.file = file;
061                this.path = StringUtils.cleanPath(file.getPath());
062        }
063
064        /**
065         * Create a new {@code FileSystemResource} from a file path.
066         * <p>Note: When building relative resources via {@link #createRelative},
067         * it makes a difference whether the specified resource base path here
068         * ends with a slash or not. In the case of "C:/dir1/", relative paths
069         * will be built underneath that root: e.g. relative path "dir2" ->
070         * "C:/dir1/dir2". In the case of "C:/dir1", relative paths will apply
071         * at the same directory level: relative path "dir2" -> "C:/dir2".
072         * @param path a file path
073         */
074        public FileSystemResource(String path) {
075                Assert.notNull(path, "Path must not be null");
076                this.file = new File(path);
077                this.path = StringUtils.cleanPath(path);
078        }
079
080
081        /**
082         * Return the file path for this resource.
083         */
084        public final String getPath() {
085                return this.path;
086        }
087
088        /**
089         * This implementation returns whether the underlying file exists.
090         * @see java.io.File#exists()
091         */
092        @Override
093        public boolean exists() {
094                return this.file.exists();
095        }
096
097        /**
098         * This implementation checks whether the underlying file is marked as readable
099         * (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
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         */
112        @Override
113        public InputStream getInputStream() throws IOException {
114                return new FileInputStream(this.file);
115        }
116
117        /**
118         * This implementation checks whether the underlying file is marked as writable
119         * (and corresponds to an actual file with content, not to a directory).
120         * @see java.io.File#canWrite()
121         * @see java.io.File#isDirectory()
122         */
123        @Override
124        public boolean isWritable() {
125                return (this.file.canWrite() && !this.file.isDirectory());
126        }
127
128        /**
129         * This implementation opens a FileOutputStream for the underlying file.
130         * @see java.io.FileOutputStream
131         */
132        @Override
133        public OutputStream getOutputStream() throws IOException {
134                return new FileOutputStream(this.file);
135        }
136
137        /**
138         * This implementation returns a URL for the underlying file.
139         * @see java.io.File#toURI()
140         */
141        @Override
142        public URL getURL() throws IOException {
143                return this.file.toURI().toURL();
144        }
145
146        /**
147         * This implementation returns a URI for the underlying file.
148         * @see java.io.File#toURI()
149         */
150        @Override
151        public URI getURI() throws IOException {
152                return this.file.toURI();
153        }
154
155        /**
156         * This implementation returns the underlying File reference.
157         */
158        @Override
159        public File getFile() {
160                return this.file;
161        }
162
163        /**
164         * This implementation returns the underlying File's length.
165         */
166        @Override
167        public long contentLength() throws IOException {
168                return this.file.length();
169        }
170
171        /**
172         * This implementation creates a FileSystemResource, applying the given path
173         * relative to the path of the underlying file of this resource descriptor.
174         * @see org.springframework.util.StringUtils#applyRelativePath(String, String)
175         */
176        @Override
177        public Resource createRelative(String relativePath) {
178                String pathToUse = StringUtils.applyRelativePath(this.path, relativePath);
179                return new FileSystemResource(pathToUse);
180        }
181
182        /**
183         * This implementation returns the name of the file.
184         * @see java.io.File#getName()
185         */
186        @Override
187        public String getFilename() {
188                return this.file.getName();
189        }
190
191        /**
192         * This implementation returns a description that includes the absolute
193         * path of the file.
194         * @see java.io.File#getAbsolutePath()
195         */
196        @Override
197        public String getDescription() {
198                return "file [" + this.file.getAbsolutePath() + "]";
199        }
200
201
202        /**
203         * This implementation compares the underlying File references.
204         */
205        @Override
206        public boolean equals(Object obj) {
207                return (obj == this ||
208                        (obj instanceof FileSystemResource && this.path.equals(((FileSystemResource) obj).path)));
209        }
210
211        /**
212         * This implementation returns the hash code of the underlying File reference.
213         */
214        @Override
215        public int hashCode() {
216                return this.path.hashCode();
217        }
218
219}