Skip to content

Commit 5006dcd

Browse files
committed
Merge pull request #295 from marschall/SPR-10608
# By Philippe Marschall * SPR-10608-rebase: Introduce java.nio.file.Path based Resource
2 parents 1f5467a + 2313c9a commit 5006dcd

File tree

3 files changed

+531
-0
lines changed

3 files changed

+531
-0
lines changed
Lines changed: 253 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,253 @@
1+
/*
2+
* Copyright 2002-2013 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.FileNotFoundException;
21+
import java.io.IOException;
22+
import java.io.InputStream;
23+
import java.io.OutputStream;
24+
import java.net.URI;
25+
import java.net.URL;
26+
import java.nio.file.Files;
27+
import java.nio.file.OpenOption;
28+
import java.nio.file.Path;
29+
import java.nio.file.Paths;
30+
31+
import org.springframework.util.Assert;
32+
33+
/**
34+
* {@link Resource} implementation for {@code java.nio.file.Path} handles.
35+
* Supports resolution as File, and also as URL.
36+
* Implements the extended {@link WritableResource} interface.
37+
*
38+
* @author Philippe Marschall
39+
* @since 4.0
40+
* @see java.nio.file.Path
41+
*/
42+
public class PathResource extends AbstractResource implements WritableResource {
43+
44+
private final Path path;
45+
46+
47+
/**
48+
* Create a new PathResource from a Path handle.
49+
* <p>Note: Unlike {@link FileSystemResource}, when building relative resources
50+
* via {@link #createRelative}, the relative path will be built <i>underneath</i> the
51+
* given root:
52+
* e.g. Paths.get("C:/dir1/"), relative path "dir2" -> "C:/dir1/dir2"!
53+
* @param path a Path handle
54+
*/
55+
public PathResource(Path path) {
56+
Assert.notNull(path, "Path must not be null");
57+
this.path = path.normalize();
58+
}
59+
60+
/**
61+
* Create a new PathResource from a Path handle.
62+
* Create a new PathResource from a Path handle.
63+
* <p>Note: Unlike {@link FileSystemResource}, when building relative resources
64+
* via {@link #createRelative}, the relative path will be built <i>underneath</i> the
65+
* given root:
66+
* e.g. Paths.get("C:/dir1/"), relative path "dir2" -> "C:/dir1/dir2"!
67+
* @param path a path
68+
* @see java.nio.file.Paths#get(String, String...)
69+
*/
70+
public PathResource(String path) {
71+
Assert.notNull(path, "Path must not be null");
72+
this.path = Paths.get(path).normalize();
73+
}
74+
75+
/**
76+
* Create a new PathResource from a Path handle.
77+
* <p>Note: Unlike {@link FileSystemResource}, when building relative resources
78+
* via {@link #createRelative}, the relative path will be built <i>underneath</i> the
79+
* given root:
80+
* e.g. Paths.get("C:/dir1/"), relative path "dir2" -> "C:/dir1/dir2"!
81+
* @see java.nio.file.Paths#get(URI)
82+
* @param uri a path URI
83+
*/
84+
public PathResource(URI uri) {
85+
Assert.notNull(uri, "URI must not be null");
86+
this.path = Paths.get(uri).normalize();
87+
}
88+
89+
90+
/**
91+
* Return the file path for this resource.
92+
*/
93+
public final String getPath() {
94+
return this.path.toString();
95+
}
96+
97+
/**
98+
* This implementation returns whether the underlying file exists.
99+
* @see org.springframework.core.io.PathResource#exists()
100+
*/
101+
@Override
102+
public boolean exists() {
103+
return Files.exists(this.path);
104+
}
105+
106+
/**
107+
* This implementation checks whether the underlying file is marked as readable
108+
* (and corresponds to an actual file with content, not to a directory).
109+
* @see java.nio.file.Files#isReadable(Path)
110+
* @see java.nio.file.Files#isDirectory(Path, java.nio.file.LinkOption...)
111+
*/
112+
@Override
113+
public boolean isReadable() {
114+
return (Files.isReadable(this.path) && !Files.isDirectory(this.path));
115+
}
116+
117+
/**
118+
* This implementation opens a InputStream for the underlying file.
119+
* @see java.nio.file.spi.FileSystemProvider#newInputStream(Path, OpenOption...)
120+
*/
121+
@Override
122+
public InputStream getInputStream() throws IOException {
123+
if(!exists()) {
124+
throw new FileNotFoundException(getPath() + " (No such file or directory)");
125+
}
126+
if(Files.isDirectory(this.path)) {
127+
throw new FileNotFoundException(getPath() + " (Is a directory)");
128+
}
129+
return Files.newInputStream(this.path);
130+
}
131+
132+
/**
133+
* This implementation returns a URL for the underlying file.
134+
* @see java.nio.file.Path#toUri()
135+
* @see java.net.URI#toURL()
136+
*/
137+
@Override
138+
public URL getURL() throws IOException {
139+
return this.path.toUri().toURL();
140+
}
141+
142+
/**
143+
* This implementation returns a URI for the underlying file.
144+
* @see java.nio.file.Path#toUri()
145+
*/
146+
@Override
147+
public URI getURI() throws IOException {
148+
return this.path.toUri();
149+
}
150+
151+
/**
152+
* This implementation returns the underlying File reference.
153+
*/
154+
@Override
155+
public File getFile() throws IOException {
156+
try {
157+
return this.path.toFile();
158+
}
159+
catch (UnsupportedOperationException ex) {
160+
// only Paths on the default file system can be converted to a File
161+
// do exception translation for cases where conversion is not possible
162+
throw new FileNotFoundException(this.path + " cannot be resolved to "
163+
+ "absolute file path");
164+
}
165+
}
166+
167+
/**
168+
* This implementation returns the underlying File's length.
169+
*/
170+
@Override
171+
public long contentLength() throws IOException {
172+
return Files.size(this.path);
173+
}
174+
175+
/**
176+
* This implementation returns the underlying File's timestamp.
177+
* @see java.nio.file.Files#getLastModifiedTime(Path, java.nio.file.LinkOption...)
178+
*/
179+
@Override
180+
public long lastModified() throws IOException {
181+
// we can not use the super class method since it uses conversion to a File and
182+
// only Paths on the default file system can be converted to a File
183+
return Files.getLastModifiedTime(path).toMillis();
184+
}
185+
186+
/**
187+
* This implementation creates a FileResource, applying the given path
188+
* relative to the path of the underlying file of this resource descriptor.
189+
* @see java.nio.file.Path#resolve(String)
190+
*/
191+
@Override
192+
public Resource createRelative(String relativePath) throws IOException {
193+
return new PathResource(this.path.resolve(relativePath));
194+
}
195+
196+
/**
197+
* This implementation returns the name of the file.
198+
* @see java.nio.file.Path#getFileName()
199+
*/
200+
@Override
201+
public String getFilename() {
202+
return this.path.getFileName().toString();
203+
}
204+
205+
@Override
206+
public String getDescription() {
207+
return "path [" + this.path.toAbsolutePath() + "]";
208+
}
209+
210+
// implementation of WritableResource
211+
212+
/**
213+
* This implementation checks whether the underlying file is marked as writable
214+
* (and corresponds to an actual file with content, not to a directory).
215+
* @see java.nio.file.Files#isWritable(Path)
216+
* @see java.nio.file.Files#isDirectory(Path, java.nio.file.LinkOption...)
217+
*/
218+
@Override
219+
public boolean isWritable() {
220+
return Files.isWritable(this.path) && !Files.isDirectory(this.path);
221+
}
222+
223+
/**
224+
* This implementation opens a OutputStream for the underlying file.
225+
* @see java.nio.file.spi.FileSystemProvider#newOutputStream(Path, OpenOption...)
226+
*/
227+
@Override
228+
public OutputStream getOutputStream() throws IOException {
229+
if(Files.isDirectory(this.path)) {
230+
throw new FileNotFoundException(getPath() + " (Is a directory)");
231+
}
232+
return Files.newOutputStream(this.path);
233+
}
234+
235+
236+
/**
237+
* This implementation compares the underlying Path references.
238+
*/
239+
@Override
240+
public boolean equals(Object obj) {
241+
return (obj == this ||
242+
(obj instanceof PathResource && this.path.equals(((PathResource) obj).path)));
243+
}
244+
245+
/**
246+
* This implementation returns the hash code of the underlying Path reference.
247+
*/
248+
@Override
249+
public int hashCode() {
250+
return this.path.hashCode();
251+
}
252+
253+
}

spring-core/src/main/java/org/springframework/core/io/Resource.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
* @see UrlResource
4343
* @see ByteArrayResource
4444
* @see InputStreamResource
45+
* @see PathResource
4546
*/
4647
public interface Resource extends InputStreamSource {
4748

0 commit comments

Comments
 (0)