diff --git a/spring-core/src/main/java/org/springframework/core/io/AbstractFileResolvingResource.java b/spring-core/src/main/java/org/springframework/core/io/AbstractFileResolvingResource.java index 9b192fd4dd..29aec14530 100644 --- a/spring-core/src/main/java/org/springframework/core/io/AbstractFileResolvingResource.java +++ b/spring-core/src/main/java/org/springframework/core/io/AbstractFileResolvingResource.java @@ -17,7 +17,6 @@ package org.springframework.core.io; import java.io.File; -import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; @@ -25,7 +24,9 @@ import java.net.HttpURLConnection; import java.net.URI; import java.net.URL; import java.net.URLConnection; +import java.nio.channels.FileChannel; import java.nio.channels.ReadableByteChannel; +import java.nio.file.StandardOpenOption; import org.springframework.util.ResourceUtils; @@ -127,7 +128,7 @@ public abstract class AbstractFileResolvingResource extends AbstractResource { @Override public ReadableByteChannel readableChannel() throws IOException { if (isFile()) { - return new FileInputStream(getFile()).getChannel(); + return FileChannel.open(getFile().toPath(), StandardOpenOption.READ); } else { return super.readableChannel(); diff --git a/spring-core/src/main/java/org/springframework/core/io/FileSystemResource.java b/spring-core/src/main/java/org/springframework/core/io/FileSystemResource.java index a3b216864a..80f86d85ea 100644 --- a/spring-core/src/main/java/org/springframework/core/io/FileSystemResource.java +++ b/spring-core/src/main/java/org/springframework/core/io/FileSystemResource.java @@ -17,15 +17,16 @@ package org.springframework.core.io; import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; 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.Files; +import java.nio.file.StandardOpenOption; import org.springframework.util.Assert; import org.springframework.util.StringUtils; @@ -35,9 +36,15 @@ import org.springframework.util.StringUtils; * Supports resolution as a {@code File} and also as a {@code URL}. * Implements the extended {@link WritableResource} interface. * + *

Note: As of Spring Framework 5.0, this {@link Resource} implementation + * uses NIO.2 API for read/write interactions. Nevertheless, in contrast to + * {@link PathResource}, it primarily manages a {@code java.io.File} handle. + * * @author Juergen Hoeller * @since 28.12.2003 + * @see PathResource * @see java.io.File + * @see java.nio.file.Files */ public class FileSystemResource extends AbstractResource implements WritableResource { @@ -113,7 +120,7 @@ public class FileSystemResource extends AbstractResource implements WritableReso */ @Override public InputStream getInputStream() throws IOException { - return new FileInputStream(this.file); + return Files.newInputStream(this.file.toPath()); } /** @@ -133,7 +140,7 @@ public class FileSystemResource extends AbstractResource implements WritableReso */ @Override public OutputStream getOutputStream() throws IOException { - return new FileOutputStream(this.file); + return Files.newOutputStream(this.file.toPath()); } /** @@ -176,7 +183,7 @@ public class FileSystemResource extends AbstractResource implements WritableReso */ @Override public ReadableByteChannel readableChannel() throws IOException { - return new FileInputStream(this.file).getChannel(); + return FileChannel.open(getFile().toPath(), StandardOpenOption.READ); } /** @@ -185,7 +192,7 @@ public class FileSystemResource extends AbstractResource implements WritableReso */ @Override public WritableByteChannel writableChannel() throws IOException { - return new FileOutputStream(this.file).getChannel(); + return FileChannel.open(getFile().toPath(), StandardOpenOption.WRITE); } /** diff --git a/spring-core/src/main/java/org/springframework/core/io/PathResource.java b/spring-core/src/main/java/org/springframework/core/io/PathResource.java index 571479dcb2..a63c22fb97 100644 --- a/spring-core/src/main/java/org/springframework/core/io/PathResource.java +++ b/spring-core/src/main/java/org/springframework/core/io/PathResource.java @@ -41,7 +41,9 @@ import org.springframework.util.Assert; * @author Philippe Marschall * @author Juergen Hoeller * @since 4.0 + * @see FileSystemResource * @see java.nio.file.Path + * @see java.nio.file.Files */ public class PathResource extends AbstractResource implements WritableResource { @@ -52,8 +54,7 @@ public class PathResource extends AbstractResource implements WritableResource { * Create a new PathResource from a Path handle. *

Note: Unlike {@link FileSystemResource}, when building relative resources * via {@link #createRelative}, the relative path will be built underneath - * the given root: - * e.g. Paths.get("C:/dir1/"), relative path "dir2" -> "C:/dir1/dir2"! + * the given root: e.g. Paths.get("C:/dir1/"), relative path "dir2" -> "C:/dir1/dir2"! * @param path a Path handle */ public PathResource(Path path) { @@ -65,8 +66,7 @@ public class PathResource extends AbstractResource implements WritableResource { * Create a new PathResource from a Path handle. *

Note: Unlike {@link FileSystemResource}, when building relative resources * via {@link #createRelative}, the relative path will be built underneath - * the given root: - * e.g. Paths.get("C:/dir1/"), relative path "dir2" -> "C:/dir1/dir2"! + * the given root: e.g. Paths.get("C:/dir1/"), relative path "dir2" -> "C:/dir1/dir2"! * @param path a path * @see java.nio.file.Paths#get(String, String...) */ @@ -79,8 +79,7 @@ public class PathResource extends AbstractResource implements WritableResource { * Create a new PathResource from a Path handle. *

Note: Unlike {@link FileSystemResource}, when building relative resources * via {@link #createRelative}, the relative path will be built underneath - * the given root: - * e.g. Paths.get("C:/dir1/"), relative path "dir2" -> "C:/dir1/dir2"! + * the given root: e.g. Paths.get("C:/dir1/"), relative path "dir2" -> "C:/dir1/dir2"! * @see java.nio.file.Paths#get(URI) * @param uri a path URI */ @@ -193,7 +192,7 @@ public class PathResource extends AbstractResource implements WritableResource { catch (UnsupportedOperationException ex) { // Only paths on the default file system can be converted to a File: // Do exception translation for cases where conversion is not possible. - throw new FileNotFoundException(this.path + " cannot be resolved to " + "absolute file path"); + throw new FileNotFoundException(this.path + " cannot be resolved to absolute file path"); } } @@ -231,11 +230,11 @@ public class PathResource extends AbstractResource implements WritableResource { public long lastModified() throws IOException { // We can not use the superclass method since it uses conversion to a File and // only a Path on the default file system can be converted to a File... - return Files.getLastModifiedTime(path).toMillis(); + return Files.getLastModifiedTime(this.path).toMillis(); } /** - * This implementation creates a FileResource, applying the given path + * This implementation creates a PathResource, applying the given path * relative to the path of the underlying file of this resource descriptor. * @see java.nio.file.Path#resolve(String) */ diff --git a/spring-core/src/main/java/org/springframework/util/FileCopyUtils.java b/spring-core/src/main/java/org/springframework/util/FileCopyUtils.java index 3c933b23b5..ed596a173b 100644 --- a/spring-core/src/main/java/org/springframework/util/FileCopyUtils.java +++ b/spring-core/src/main/java/org/springframework/util/FileCopyUtils.java @@ -16,19 +16,16 @@ package org.springframework.util; -import java.io.BufferedInputStream; -import java.io.BufferedOutputStream; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.Reader; import java.io.StringWriter; import java.io.Writer; +import java.nio.file.Files; import org.springframework.lang.Nullable; @@ -42,6 +39,7 @@ import org.springframework.lang.Nullable; * @author Juergen Hoeller * @since 06.10.2003 * @see StreamUtils + * @see FileSystemUtils */ public abstract class FileCopyUtils { @@ -62,9 +60,7 @@ public abstract class FileCopyUtils { public static int copy(File in, File out) throws IOException { Assert.notNull(in, "No input File specified"); Assert.notNull(out, "No output File specified"); - - return copy(new BufferedInputStream(new FileInputStream(in)), - new BufferedOutputStream(new FileOutputStream(out))); + return copy(Files.newInputStream(in.toPath()), Files.newOutputStream(out.toPath())); } /** @@ -76,10 +72,7 @@ public abstract class FileCopyUtils { public static void copy(byte[] in, File out) throws IOException { Assert.notNull(in, "No input byte array specified"); Assert.notNull(out, "No output File specified"); - - ByteArrayInputStream inStream = new ByteArrayInputStream(in); - OutputStream outStream = new BufferedOutputStream(new FileOutputStream(out)); - copy(inStream, outStream); + copy(new ByteArrayInputStream(in), Files.newOutputStream(out.toPath())); } /** @@ -90,8 +83,7 @@ public abstract class FileCopyUtils { */ public static byte[] copyToByteArray(File in) throws IOException { Assert.notNull(in, "No input File specified"); - - return copyToByteArray(new BufferedInputStream(new FileInputStream(in))); + return copyToByteArray(Files.newInputStream(in.toPath())); } diff --git a/spring-core/src/main/java/org/springframework/util/FileSystemUtils.java b/spring-core/src/main/java/org/springframework/util/FileSystemUtils.java index 5445f006f1..96c5424d3f 100644 --- a/spring-core/src/main/java/org/springframework/util/FileSystemUtils.java +++ b/spring-core/src/main/java/org/springframework/util/FileSystemUtils.java @@ -18,6 +18,11 @@ package org.springframework.util; import java.io.File; import java.io.IOException; +import java.nio.file.FileVisitResult; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.SimpleFileVisitor; +import java.nio.file.attribute.BasicFileAttributes; import org.springframework.lang.Nullable; @@ -27,29 +32,60 @@ import org.springframework.lang.Nullable; * @author Rob Harrop * @author Juergen Hoeller * @since 2.5.3 - * @deprecated as of Spring Framework 5.0, in favor of native NIO API usage + * @see java.io.File + * @see java.nio.file.Path + * @see java.nio.file.Files */ -@Deprecated public abstract class FileSystemUtils { /** * Delete the supplied {@link File} - for directories, * recursively delete any nested directories or files as well. + *

Note: Like {@link File#delete()}, this method does not throw any + * exception but rather silently returns {@code false} in case of I/O + * errors. Consider using {@link #deleteRecursively(Path)} for NIO-style + * handling of I/O errors, clearly differentiating between non-existence + * and failure to delete an existing file. * @param root the root {@code File} to delete - * @return {@code true} if the {@code File} was deleted, + * @return {@code true} if the {@code File} was successfully deleted, * otherwise {@code false} */ public static boolean deleteRecursively(@Nullable File root) { - if (root != null && root.exists()) { - if (root.isDirectory()) { - File[] children = root.listFiles(); - if (children != null) { - for (File child : children) { - deleteRecursively(child); - } - } + if (root != null) { + try { + return deleteRecursively(root.toPath()); } - return root.delete(); + catch (IOException ex) { + return false; + } + } + return false; + } + + /** + * Delete the supplied {@link File} - for directories, + * recursively delete any nested directories or files as well. + * @param root the root {@code File} to delete + * @return {@code true} if the {@code File} existed and was deleted, + * or {@code false} it it did not exist + * @throws IOException in the case of I/O errors + * @since 5.0 + */ + public static boolean deleteRecursively(@Nullable Path root) throws IOException { + if (root != null) { + Files.walkFileTree(root, new SimpleFileVisitor() { + @Override + public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { + Files.delete(file); + return FileVisitResult.CONTINUE; + } + @Override + public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException { + Files.delete(dir); + return FileVisitResult.CONTINUE; + } + }); + return Files.deleteIfExists(root); } return false; } @@ -61,43 +97,44 @@ public abstract class FileSystemUtils { * @param dest the destination directory * @throws IOException in the case of I/O errors */ - public static void copyRecursively(@Nullable File src, File dest) throws IOException { - Assert.isTrue(src != null && (src.isDirectory() || src.isFile()), - "Source File must denote a directory or file"); + public static void copyRecursively(File src, File dest) throws IOException { + Assert.notNull(src, "Source File must not be null"); Assert.notNull(dest, "Destination File must not be null"); - doCopyRecursively(src, dest); + copyRecursively(src.toPath(), dest.toPath()); } /** - * Actually copy the contents of the {@code src} file/directory + * Recursively copy the contents of the {@code src} file/directory * to the {@code dest} file/directory. * @param src the source directory * @param dest the destination directory * @throws IOException in the case of I/O errors + * @since 5.0 */ - private static void doCopyRecursively(File src, File dest) throws IOException { - if (src.isDirectory()) { - dest.mkdir(); - File[] entries = src.listFiles(); - if (entries == null) { - throw new IOException("Could not list files in directory: " + src); - } - for (File entry : entries) { - doCopyRecursively(entry, new File(dest, entry.getName())); - } + public static void copyRecursively(Path src, Path dest) throws IOException { + Assert.notNull(src, "Source Path must not be null"); + Assert.notNull(dest, "Destination Path must not be null"); + BasicFileAttributes srcAttr = Files.readAttributes(src, BasicFileAttributes.class); + + if (srcAttr.isDirectory()) { + Files.walkFileTree(src, new SimpleFileVisitor() { + @Override + public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException { + Files.createDirectories(dest.resolve(src.relativize(dir))); + return FileVisitResult.CONTINUE; + } + @Override + public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { + Files.copy(file, dest.resolve(src.relativize(file))); + return FileVisitResult.CONTINUE; + } + }); } - else if (src.isFile()) { - try { - dest.createNewFile(); - } - catch (IOException ex) { - throw new IOException("Failed to create file: " + dest, ex); - } - FileCopyUtils.copy(src, dest); + else if (srcAttr.isRegularFile()) { + Files.copy(src, dest); } else { - // Special File handle: neither a file not a directory. - // Simply skip it when contained in nested directory... + throw new IllegalArgumentException("Source File must denote a directory or file"); } } diff --git a/spring-core/src/test/java/org/springframework/util/FileSystemUtilsTests.java b/spring-core/src/test/java/org/springframework/util/FileSystemUtilsTests.java index dd56271ac3..e5464dbb33 100644 --- a/spring-core/src/test/java/org/springframework/util/FileSystemUtilsTests.java +++ b/spring-core/src/test/java/org/springframework/util/FileSystemUtilsTests.java @@ -26,7 +26,6 @@ import static org.junit.Assert.*; /** * @author Rob Harrop */ -@Deprecated public class FileSystemUtilsTests { @Test diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/standard/SpelCompiler.java b/spring-expression/src/main/java/org/springframework/expression/spel/standard/SpelCompiler.java index fd77cfd8d2..bc55559c2b 100644 --- a/spring-expression/src/main/java/org/springframework/expression/spel/standard/SpelCompiler.java +++ b/spring-expression/src/main/java/org/springframework/expression/spel/standard/SpelCompiler.java @@ -16,9 +16,6 @@ package org.springframework.expression.spel.standard; -import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; import java.net.URL; import java.net.URLClassLoader; import java.util.Map; @@ -244,40 +241,6 @@ public class SpelCompiler implements Opcodes { } } - /** - * For debugging purposes, dump the specified byte code into a file on the disk. - * Not yet hooked in, needs conditionally calling based on a sys prop. - * @param expressionText the text of the expression compiled - * @param name the name of the class being used for the compiled expression - * @param bytecode the bytecode for the generated class - */ - @SuppressWarnings("unused") - private static void dump(String expressionText, String name, byte[] bytecode) { - String nameToUse = name.replace('.', '/'); - String dir = (nameToUse.indexOf('/') != -1 ? nameToUse.substring(0, nameToUse.lastIndexOf('/')) : ""); - String dumpLocation = null; - try { - File tempFile = File.createTempFile("tmp", null); - dumpLocation = tempFile + File.separator + nameToUse + ".class"; - tempFile.delete(); - File f = new File(tempFile, dir); - f.mkdirs(); - // System.out.println("Expression '" + expressionText + "' compiled code dumped to " + dumpLocation); - if (logger.isDebugEnabled()) { - logger.debug("Expression '" + expressionText + "' compiled code dumped to " + dumpLocation); - } - f = new File(dumpLocation); - FileOutputStream fos = new FileOutputStream(f); - fos.write(bytecode); - fos.flush(); - fos.close(); - } - catch (IOException ex) { - throw new IllegalStateException( - "Unexpected problem dumping class '" + nameToUse + "' into " + dumpLocation, ex); - } - } - /** * A ChildClassLoader will load the generated compiled expression classes. diff --git a/spring-web/src/main/java/org/springframework/http/codec/multipart/SynchronossPartHttpMessageReader.java b/spring-web/src/main/java/org/springframework/http/codec/multipart/SynchronossPartHttpMessageReader.java index ecb56ff81c..613ad3e122 100644 --- a/spring-web/src/main/java/org/springframework/http/codec/multipart/SynchronossPartHttpMessageReader.java +++ b/spring-web/src/main/java/org/springframework/http/codec/multipart/SynchronossPartHttpMessageReader.java @@ -17,7 +17,6 @@ package org.springframework.http.codec.multipart; import java.io.File; -import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.nio.channels.Channels; @@ -25,6 +24,7 @@ import java.nio.channels.FileChannel; import java.nio.channels.ReadableByteChannel; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; +import java.nio.file.StandardOpenOption; import java.util.Collections; import java.util.List; import java.util.Map; @@ -187,9 +187,9 @@ public class SynchronossPartHttpMessageReader implements HttpMessageReader } private Part createPart(StreamStorage storage, HttpHeaders httpHeaders) { - String fileName = MultipartUtils.getFileName(httpHeaders); - if (fileName != null) { - return new SynchronossFilePart(httpHeaders, storage, fileName, this.bufferFactory); + String filename = MultipartUtils.getFileName(httpHeaders); + if (filename != null) { + return new SynchronossFilePart(httpHeaders, storage, this.bufferFactory, filename); } else if (MultipartUtils.isFormField(httpHeaders, this.context)) { String value = MultipartUtils.readFormParameterValue(storage, httpHeaders); @@ -200,13 +200,6 @@ public class SynchronossPartHttpMessageReader implements HttpMessageReader } } - private Part createPart(HttpHeaders httpHeaders, StreamStorage storage) { - String fileName = MultipartUtils.getFileName(httpHeaders); - return fileName != null ? - new SynchronossFilePart(httpHeaders, storage, fileName, this.bufferFactory) : - new DefaultSynchronossPart(httpHeaders, storage, this.bufferFactory); - } - @Override public void onError(String message, Throwable cause) { if (this.terminated.getAndIncrement() == 0) { @@ -284,15 +277,18 @@ public class SynchronossPartHttpMessageReader implements HttpMessageReader private static class SynchronossFilePart extends DefaultSynchronossPart implements FilePart { - public SynchronossFilePart(HttpHeaders headers, StreamStorage storage, - String fileName, DataBufferFactory factory) { + private final String filename; + + public SynchronossFilePart( + HttpHeaders headers, StreamStorage storage, DataBufferFactory factory, String filename) { super(headers, storage, factory); + this.filename = filename; } @Override public String filename() { - return MultipartUtils.getFileName(headers()); + return this.filename; } @Override @@ -301,8 +297,7 @@ public class SynchronossPartHttpMessageReader implements HttpMessageReader FileChannel output = null; try { input = Channels.newChannel(getStorage().getInputStream()); - output = new FileOutputStream(destination).getChannel(); - + output = FileChannel.open(destination.toPath(), StandardOpenOption.WRITE); long size = (input instanceof FileChannel ? ((FileChannel) input).size() : Long.MAX_VALUE); long totalWritten = 0; while (totalWritten < size) { diff --git a/spring-web/src/main/java/org/springframework/http/server/reactive/UndertowServerHttpResponse.java b/spring-web/src/main/java/org/springframework/http/server/reactive/UndertowServerHttpResponse.java index d0bc832fa6..ebc9fb2339 100644 --- a/spring-web/src/main/java/org/springframework/http/server/reactive/UndertowServerHttpResponse.java +++ b/spring-web/src/main/java/org/springframework/http/server/reactive/UndertowServerHttpResponse.java @@ -17,10 +17,10 @@ package org.springframework.http.server.reactive; import java.io.File; -import java.io.FileInputStream; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.channels.FileChannel; +import java.nio.file.StandardOpenOption; import java.util.List; import java.util.Map; @@ -85,7 +85,7 @@ public class UndertowServerHttpResponse extends AbstractListenerServerHttpRespon return doCommit(() -> { FileChannel source = null; try { - source = new FileInputStream(file).getChannel(); + source = FileChannel.open(file.toPath(), StandardOpenOption.READ); StreamSinkChannel destination = getUndertowExchange().getResponseChannel(); Channels.transferBlocking(destination, source, position, count); return Mono.empty(); diff --git a/spring-web/src/main/java/org/springframework/web/multipart/support/StandardMultipartHttpServletRequest.java b/spring-web/src/main/java/org/springframework/web/multipart/support/StandardMultipartHttpServletRequest.java index aed3c95e22..655c9f07cd 100644 --- a/spring-web/src/main/java/org/springframework/web/multipart/support/StandardMultipartHttpServletRequest.java +++ b/spring-web/src/main/java/org/springframework/web/multipart/support/StandardMultipartHttpServletRequest.java @@ -17,12 +17,12 @@ package org.springframework.web.multipart.support; import java.io.File; -import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.Serializable; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; +import java.nio.file.Files; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; @@ -317,7 +317,7 @@ public class StandardMultipartHttpServletRequest extends AbstractMultipartHttpSe // At least we offloaded the file from memory storage; it'll get deleted // from the temp dir eventually in any case. And for our user's purposes, // we can manually copy it to the requested location as a fallback. - FileCopyUtils.copy(this.part.getInputStream(), new FileOutputStream(dest)); + FileCopyUtils.copy(this.part.getInputStream(), Files.newOutputStream(dest.toPath())); } } }