diff --git a/spring-core/src/main/java/org/springframework/core/io/Resource.java b/spring-core/src/main/java/org/springframework/core/io/Resource.java
index a492ebc2ffc..9e13d4961f6 100644
--- a/spring-core/src/main/java/org/springframework/core/io/Resource.java
+++ b/spring-core/src/main/java/org/springframework/core/io/Resource.java
@@ -117,8 +117,10 @@ public interface Resource extends InputStreamSource {
/**
* Return a File handle for this resource.
- * @throws java.io.FileNotFoundException if the resource cannot be resolved as
- * absolute file path, i.e. if the resource is not available in a file system
+ *
Note: This only works for files in the default file system.
+ * @throws UnsupportedOperationException if the resource is a file but cannot be
+ * exposed as a {@code java.io.File}; an alternative to {@code FileNotFoundException}
+ * @throws java.io.FileNotFoundException if the resource cannot be resolved as a file
* @throws IOException in case of general resolution/reading failures
* @see #getInputStream()
*/
diff --git a/spring-core/src/main/java/org/springframework/core/io/buffer/DataBufferUtils.java b/spring-core/src/main/java/org/springframework/core/io/buffer/DataBufferUtils.java
index f705f16a935..568c7be82c5 100644
--- a/spring-core/src/main/java/org/springframework/core/io/buffer/DataBufferUtils.java
+++ b/spring-core/src/main/java/org/springframework/core/io/buffer/DataBufferUtils.java
@@ -16,7 +16,6 @@
package org.springframework.core.io.buffer;
-import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
@@ -223,17 +222,17 @@ public abstract class DataBufferUtils {
try {
if (resource.isFile()) {
- File file = resource.getFile();
+ Path filePath = resource.getFile().toPath();
return readAsynchronousFileChannel(
- () -> AsynchronousFileChannel.open(file.toPath(), StandardOpenOption.READ),
+ () -> AsynchronousFileChannel.open(filePath, StandardOpenOption.READ),
position, bufferFactory, bufferSize);
}
}
- catch (IOException ignore) {
+ catch (IOException | UnsupportedOperationException ignore) {
// fallback to resource.readableChannel(), below
}
Flux result = readByteChannel(resource::readableChannel, bufferFactory, bufferSize);
- return position == 0 ? result : skipUntilByteCount(result, position);
+ return (position == 0 ? result : skipUntilByteCount(result, position));
}
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 be2ccc590b2..261c5574cc9 100644
--- a/spring-core/src/main/java/org/springframework/util/FileSystemUtils.java
+++ b/spring-core/src/main/java/org/springframework/util/FileSystemUtils.java
@@ -86,13 +86,12 @@ public abstract class FileSystemUtils {
Files.walkFileTree(root, new SimpleFileVisitor<>() {
@Override
- public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
+ public FileVisitResult visitFile(Path file, BasicFileAttributes attr) throws IOException {
Files.delete(file);
return FileVisitResult.CONTINUE;
}
-
@Override
- public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
+ public FileVisitResult postVisitDirectory(Path dir, IOException ex) throws IOException {
Files.delete(dir);
return FileVisitResult.CONTINUE;
}
@@ -127,19 +126,34 @@ public abstract class FileSystemUtils {
BasicFileAttributes srcAttr = Files.readAttributes(src, BasicFileAttributes.class);
if (srcAttr.isDirectory()) {
- Files.walkFileTree(src, EnumSet.of(FOLLOW_LINKS), Integer.MAX_VALUE, 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)), StandardCopyOption.REPLACE_EXISTING);
- return FileVisitResult.CONTINUE;
- }
- });
+ if (src.getClass() == dest.getClass()) { // dest.resolve(Path) only works for same Path type
+ Files.walkFileTree(src, EnumSet.of(FOLLOW_LINKS), Integer.MAX_VALUE, new SimpleFileVisitor<>() {
+ @Override
+ public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attr) throws IOException {
+ Files.createDirectories(dest.resolve(src.relativize(dir)));
+ return FileVisitResult.CONTINUE;
+ }
+ @Override
+ public FileVisitResult visitFile(Path file, BasicFileAttributes attr) throws IOException {
+ Files.copy(file, dest.resolve(src.relativize(file)), StandardCopyOption.REPLACE_EXISTING);
+ return FileVisitResult.CONTINUE;
+ }
+ });
+ }
+ else { // use dest.resolve(String) for different Path types
+ Files.walkFileTree(src, EnumSet.of(FOLLOW_LINKS), Integer.MAX_VALUE, new SimpleFileVisitor<>() {
+ @Override
+ public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attr) throws IOException {
+ Files.createDirectories(dest.resolve(src.relativize(dir).toString()));
+ return FileVisitResult.CONTINUE;
+ }
+ @Override
+ public FileVisitResult visitFile(Path file, BasicFileAttributes attr) throws IOException {
+ Files.copy(file, dest.resolve(src.relativize(file).toString()), StandardCopyOption.REPLACE_EXISTING);
+ return FileVisitResult.CONTINUE;
+ }
+ });
+ }
}
else if (srcAttr.isRegularFile()) {
Files.copy(src, dest);
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 c91ccefc164..8f8368bbf58 100644
--- a/spring-core/src/test/java/org/springframework/util/FileSystemUtilsTests.java
+++ b/spring-core/src/test/java/org/springframework/util/FileSystemUtilsTests.java
@@ -17,20 +17,29 @@
package org.springframework.util;
import java.io.File;
+import java.net.URI;
+import java.nio.file.FileSystem;
+import java.nio.file.FileSystems;
+import java.nio.file.Path;
+import java.util.Map;
-import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.io.TempDir;
import static org.assertj.core.api.Assertions.assertThat;
/**
+ * Tests for {@link FileSystemUtils}.
+ *
* @author Rob Harrop
+ * @author Sam Brannen
+ * @author Juergen Hoeller
*/
class FileSystemUtilsTests {
@Test
- void deleteRecursively() throws Exception {
- File root = new File("./tmp/root");
+ void deleteRecursively(@TempDir File tempDir) throws Exception {
+ File root = new File(tempDir, "root");
File child = new File(root, "child");
File grandchild = new File(child, "grandchild");
@@ -53,8 +62,8 @@ class FileSystemUtilsTests {
}
@Test
- void copyRecursively() throws Exception {
- File src = new File("./tmp/src");
+ void copyRecursively(@TempDir File tempDir) throws Exception {
+ File src = new File(tempDir, "src");
File child = new File(src, "child");
File grandchild = new File(child, "grandchild");
@@ -68,27 +77,25 @@ class FileSystemUtilsTests {
assertThat(grandchild).exists();
assertThat(bar).exists();
- File dest = new File("./dest");
+ File dest = new File(tempDir, "/dest");
FileSystemUtils.copyRecursively(src, dest);
assertThat(dest).exists();
- assertThat(new File(dest, child.getName())).exists();
+ assertThat(new File(dest, "child")).exists();
+ assertThat(new File(dest, "child/bar.txt")).exists();
- FileSystemUtils.deleteRecursively(src);
- assertThat(src).doesNotExist();
- }
+ URI uri = URI.create("jar:file:/" + dest.toString().replace('\\', '/') + "/archive.zip");
+ Map env = Map.of("create", "true");
+ FileSystem zipfs = FileSystems.newFileSystem(uri, env);
+ Path ziproot = zipfs.getPath("/");
+ FileSystemUtils.copyRecursively(src.toPath(), ziproot);
+ assertThat(zipfs.getPath("/child")).exists();
+ assertThat(zipfs.getPath("/child/bar.txt")).exists();
- @AfterEach
- void tearDown() {
- File tmp = new File("./tmp");
- if (tmp.exists()) {
- FileSystemUtils.deleteRecursively(tmp);
- }
- File dest = new File("./dest");
- if (dest.exists()) {
- FileSystemUtils.deleteRecursively(dest);
- }
+ zipfs.close();
+ FileSystemUtils.deleteRecursively(src);
+ assertThat(src).doesNotExist();
}
}
diff --git a/spring-web/src/main/java/org/springframework/http/codec/ResourceHttpMessageWriter.java b/spring-web/src/main/java/org/springframework/http/codec/ResourceHttpMessageWriter.java
index d5de3865b60..ddf10aa40f4 100644
--- a/spring-web/src/main/java/org/springframework/http/codec/ResourceHttpMessageWriter.java
+++ b/spring-web/src/main/java/org/springframework/http/codec/ResourceHttpMessageWriter.java
@@ -203,8 +203,8 @@ public class ResourceHttpMessageWriter implements HttpMessageWriter {
}
return zeroCopyHttpOutputMessage.writeWith(file, pos, count);
}
- catch (IOException ex) {
- // should not happen
+ catch (IOException | UnsupportedOperationException ignore) {
+ // returning null below leads to fallback code path
}
}
return null;