/*
 * Copyright 2002-2018 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.springframework.core.io;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URI;
import java.net.URL;
import java.nio.channels.FileChannel;
import java.nio.channels.ReadableByteChannel;
import java.nio.channels.WritableByteChannel;
import java.nio.file.FileSystem;
import java.nio.file.Files;
import java.nio.file.NoSuchFileException;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;

import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;

Resource implementation for java.io.File and java.nio.file.Path handles with a file system target. Supports resolution as a File and also as a URL. Implements the extended WritableResource interface.

Note: As of Spring Framework 5.0, this Resource implementation uses NIO.2 API for read/write interactions. As of 5.1, it may be constructed with a Path handle in which case it will perform all file system interactions via NIO.2, only resorting to File on getFile().

Author:Juergen Hoeller
See Also:
Since:28.12.2003
/** * {@link Resource} implementation for {@code java.io.File} and * {@code java.nio.file.Path} handles with a file system target. * Supports resolution as a {@code File} and also as a {@code URL}. * Implements the extended {@link WritableResource} interface. * * <p>Note: As of Spring Framework 5.0, this {@link Resource} implementation uses * NIO.2 API for read/write interactions. As of 5.1, it may be constructed with a * {@link java.nio.file.Path} handle in which case it will perform all file system * interactions via NIO.2, only resorting to {@link File} on {@link #getFile()}. * * @author Juergen Hoeller * @since 28.12.2003 * @see #FileSystemResource(File) * @see #FileSystemResource(Path) * @see java.io.File * @see java.nio.file.Files */
public class FileSystemResource extends AbstractResource implements WritableResource { private final String path; @Nullable private final File file; private final Path filePath;
Create a new FileSystemResource from a file path.

Note: When building relative resources via createRelative, it makes a difference whether the specified resource base path here ends with a slash or not. In the case of "C:/dir1/", relative paths will be built underneath that root: e.g. relative path "dir2" -> "C:/dir1/dir2". In the case of "C:/dir1", relative paths will apply at the same directory level: relative path "dir2" -> "C:/dir2".

Params:
  • path – a file path
See Also:
/** * Create a new {@code FileSystemResource} from a file path. * <p>Note: When building relative resources via {@link #createRelative}, * it makes a difference whether the specified resource base path here * ends with a slash or not. In the case of "C:/dir1/", relative paths * will be built underneath that root: e.g. relative path "dir2" -> * "C:/dir1/dir2". In the case of "C:/dir1", relative paths will apply * at the same directory level: relative path "dir2" -> "C:/dir2". * @param path a file path * @see #FileSystemResource(Path) */
public FileSystemResource(String path) { Assert.notNull(path, "Path must not be null"); this.path = StringUtils.cleanPath(path); this.file = new File(path); this.filePath = this.file.toPath(); }
Create a new FileSystemResource from a File handle.

Note: When building relative resources via createRelative, the relative path will apply at the same directory level: e.g. new File("C:/dir1"), relative path "dir2" -> "C:/dir2"! If you prefer to have relative paths built underneath the given root directory, use the constructor with a file path to append a trailing slash to the root path: "C:/dir1/", which indicates this directory as root for all relative paths.

Params:
  • file – a File handle
See Also:
/** * Create a new {@code FileSystemResource} from a {@link File} handle. * <p>Note: When building relative resources via {@link #createRelative}, * the relative path will apply <i>at the same directory level</i>: * e.g. new File("C:/dir1"), relative path "dir2" -> "C:/dir2"! * If you prefer to have relative paths built underneath the given root directory, * use the {@link #FileSystemResource(String) constructor with a file path} * to append a trailing slash to the root path: "C:/dir1/", which indicates * this directory as root for all relative paths. * @param file a File handle * @see #FileSystemResource(Path) * @see #getFile() */
public FileSystemResource(File file) { Assert.notNull(file, "File must not be null"); this.path = StringUtils.cleanPath(file.getPath()); this.file = file; this.filePath = file.toPath(); }
Create a new FileSystemResource from a Path handle, performing all file system interactions via NIO.2 instead of File.

In contrast to PathResource, this variant strictly follows the general FileSystemResource conventions, in particular in terms of path cleaning and createRelative(String) handling.

Params:
  • filePath – a Path handle to a file
See Also:
Since:5.1
/** * Create a new {@code FileSystemResource} from a {@link Path} handle, * performing all file system interactions via NIO.2 instead of {@link File}. * <p>In contrast to {@link PathResource}, this variant strictly follows the * general {@link FileSystemResource} conventions, in particular in terms of * path cleaning and {@link #createRelative(String)} handling. * @param filePath a Path handle to a file * @since 5.1 * @see #FileSystemResource(File) */
public FileSystemResource(Path filePath) { Assert.notNull(filePath, "Path must not be null"); this.path = StringUtils.cleanPath(filePath.toString()); this.file = null; this.filePath = filePath; }
Create a new FileSystemResource from a FileSystem handle, locating the specified path.

This is an alternative to FileSystemResource(String), performing all file system interactions via NIO.2 instead of File.

Params:
  • fileSystem – the FileSystem to locate the path within
  • path – a file path
See Also:
Since:5.1.1
/** * Create a new {@code FileSystemResource} from a {@link FileSystem} handle, * locating the specified path. * <p>This is an alternative to {@link #FileSystemResource(String)}, * performing all file system interactions via NIO.2 instead of {@link File}. * @param fileSystem the FileSystem to locate the path within * @param path a file path * @since 5.1.1 * @see #FileSystemResource(File) */
public FileSystemResource(FileSystem fileSystem, String path) { Assert.notNull(fileSystem, "FileSystem must not be null"); Assert.notNull(path, "Path must not be null"); this.path = StringUtils.cleanPath(path); this.file = null; this.filePath = fileSystem.getPath(this.path).normalize(); }
Return the file path for this resource.
/** * Return the file path for this resource. */
public final String getPath() { return this.path; }
This implementation returns whether the underlying file exists.
See Also:
  • exists.exists()
/** * This implementation returns whether the underlying file exists. * @see java.io.File#exists() */
@Override public boolean exists() { return (this.file != null ? this.file.exists() : Files.exists(this.filePath)); }
This implementation checks whether the underlying file is marked as readable (and corresponds to an actual file with content, not to a directory).
See Also:
/** * This implementation checks whether the underlying file is marked as readable * (and corresponds to an actual file with content, not to a directory). * @see java.io.File#canRead() * @see java.io.File#isDirectory() */
@Override public boolean isReadable() { return (this.file != null ? this.file.canRead() && !this.file.isDirectory() : Files.isReadable(this.filePath) && !Files.isDirectory(this.filePath)); }
This implementation opens a NIO file stream for the underlying file.
See Also:
  • FileInputStream
/** * This implementation opens a NIO file stream for the underlying file. * @see java.io.FileInputStream */
@Override public InputStream getInputStream() throws IOException { try { return Files.newInputStream(this.filePath); } catch (NoSuchFileException ex) { throw new FileNotFoundException(ex.getMessage()); } }
This implementation checks whether the underlying file is marked as writable (and corresponds to an actual file with content, not to a directory).
See Also:
/** * This implementation checks whether the underlying file is marked as writable * (and corresponds to an actual file with content, not to a directory). * @see java.io.File#canWrite() * @see java.io.File#isDirectory() */
@Override public boolean isWritable() { return (this.file != null ? this.file.canWrite() && !this.file.isDirectory() : Files.isWritable(this.filePath) && !Files.isDirectory(this.filePath)); }
This implementation opens a FileOutputStream for the underlying file.
See Also:
  • FileOutputStream
/** * This implementation opens a FileOutputStream for the underlying file. * @see java.io.FileOutputStream */
@Override public OutputStream getOutputStream() throws IOException { return Files.newOutputStream(this.filePath); }
This implementation returns a URL for the underlying file.
See Also:
  • toURI.toURI()
/** * This implementation returns a URL for the underlying file. * @see java.io.File#toURI() */
@Override public URL getURL() throws IOException { return (this.file != null ? this.file.toURI().toURL() : this.filePath.toUri().toURL()); }
This implementation returns a URI for the underlying file.
See Also:
  • toURI.toURI()
/** * This implementation returns a URI for the underlying file. * @see java.io.File#toURI() */
@Override public URI getURI() throws IOException { return (this.file != null ? this.file.toURI() : this.filePath.toUri()); }
This implementation always indicates a file.
/** * This implementation always indicates a file. */
@Override public boolean isFile() { return true; }
This implementation returns the underlying File reference.
/** * This implementation returns the underlying File reference. */
@Override public File getFile() { return (this.file != null ? this.file : this.filePath.toFile()); }
This implementation opens a FileChannel for the underlying file.
See Also:
  • FileChannel
/** * This implementation opens a FileChannel for the underlying file. * @see java.nio.channels.FileChannel */
@Override public ReadableByteChannel readableChannel() throws IOException { try { return FileChannel.open(this.filePath, StandardOpenOption.READ); } catch (NoSuchFileException ex) { throw new FileNotFoundException(ex.getMessage()); } }
This implementation opens a FileChannel for the underlying file.
See Also:
  • FileChannel
/** * This implementation opens a FileChannel for the underlying file. * @see java.nio.channels.FileChannel */
@Override public WritableByteChannel writableChannel() throws IOException { return FileChannel.open(this.filePath, StandardOpenOption.WRITE); }
This implementation returns the underlying File/Path length.
/** * This implementation returns the underlying File/Path length. */
@Override public long contentLength() throws IOException { if (this.file != null) { long length = this.file.length(); if (length == 0L && !this.file.exists()) { throw new FileNotFoundException(getDescription() + " cannot be resolved in the file system for checking its content length"); } return length; } else { try { return Files.size(this.filePath); } catch (NoSuchFileException ex) { throw new FileNotFoundException(ex.getMessage()); } } }
This implementation returns the underlying File/Path last-modified time.
/** * This implementation returns the underlying File/Path last-modified time. */
@Override public long lastModified() throws IOException { if (this.file != null) { return super.lastModified(); } else { try { return Files.getLastModifiedTime(this.filePath).toMillis(); } catch (NoSuchFileException ex) { throw new FileNotFoundException(ex.getMessage()); } } }
This implementation creates a FileSystemResource, applying the given path relative to the path of the underlying file of this resource descriptor.
See Also:
  • applyRelativePath.applyRelativePath(String, String)
/** * This implementation creates a FileSystemResource, applying the given path * relative to the path of the underlying file of this resource descriptor. * @see org.springframework.util.StringUtils#applyRelativePath(String, String) */
@Override public Resource createRelative(String relativePath) { String pathToUse = StringUtils.applyRelativePath(this.path, relativePath); return (this.file != null ? new FileSystemResource(pathToUse) : new FileSystemResource(this.filePath.getFileSystem(), pathToUse)); }
This implementation returns the name of the file.
See Also:
  • getName.getName()
/** * This implementation returns the name of the file. * @see java.io.File#getName() */
@Override public String getFilename() { return (this.file != null ? this.file.getName() : this.filePath.getFileName().toString()); }
This implementation returns a description that includes the absolute path of the file.
See Also:
  • getAbsolutePath.getAbsolutePath()
/** * This implementation returns a description that includes the absolute * path of the file. * @see java.io.File#getAbsolutePath() */
@Override public String getDescription() { return "file [" + (this.file != null ? this.file.getAbsolutePath() : this.filePath.toAbsolutePath()) + "]"; }
This implementation compares the underlying File references.
/** * This implementation compares the underlying File references. */
@Override public boolean equals(Object other) { return (this == other || (other instanceof FileSystemResource && this.path.equals(((FileSystemResource) other).path))); }
This implementation returns the hash code of the underlying File reference.
/** * This implementation returns the hash code of the underlying File reference. */
@Override public int hashCode() { return this.path.hashCode(); } }