Polishing external contribution

This commit makes several changes to PR #24651.

- Add byte[] getContentAsByteArray() on Resource.
- Remove getContentAsString() from Resource, as it relied on the default
charset which is not reliable.
- Add getContentAsString() to EncodedResource, as a charset is provided
through the constructor.

See gh-24651
This commit is contained in:
Arjen Poutsma
2023-02-14 14:28:30 +01:00
parent 4da2499613
commit 12d4dc1bae
12 changed files with 192 additions and 124 deletions

View File

@@ -26,10 +26,7 @@ import java.net.URL;
import java.net.URLConnection;
import java.nio.channels.FileChannel;
import java.nio.channels.ReadableByteChannel;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.NoSuchFileException;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.util.jar.JarEntry;
@@ -323,39 +320,6 @@ public abstract class AbstractFileResolvingResource extends AbstractResource {
protected void customizeConnection(HttpURLConnection con) throws IOException {
}
/**
* This implementation returns the contents of a file as a string using the
* system default Charset. Provided the resource exists and the context has
* access to it, the contents will be returned as a single string with line
* feed characters retained.
* @return the contents of the requested file as a {@code String}.
* @throws FileNotFoundException in the event the file path is invalid.
* @throws IOException if the file can not be read or cannot be serialzied.
*/
@Override
public String getContentAsString() throws IOException {
if( !exists() ) {
throw new FileNotFoundException(getDescription() + " cannot be found.");
}
if ( !isReadable() ) {
throw new IOException(getDescription() + " cannot be opened for reading.");
}
return new String(Files.readAllBytes(Paths.get(getFile().getAbsolutePath())), Charset.defaultCharset());
}
@Override
public String getContentAsString(Charset charset) throws IOException {
if( !exists() ) {
throw new FileNotFoundException(getDescription() + " cannot be found.");
}
if ( !isReadable() ) {
throw new IOException(getDescription() + " cannot be opened for reading.");
}
return new String(Files.readAllBytes(Paths.get(getFile().getAbsolutePath())), charset);
}
/**
* Inner delegate class, avoiding a hard JBoss VFS API dependency at runtime.

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2021 the original author or authors.
* Copyright 2002-2023 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.
@@ -19,6 +19,7 @@ package org.springframework.core.io;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.Charset;
import java.util.Arrays;
import org.springframework.lang.Nullable;
@@ -100,6 +101,19 @@ public class ByteArrayResource extends AbstractResource {
return new ByteArrayInputStream(this.byteArray);
}
@Override
public byte[] getContentAsByteArray() throws IOException {
int length = this.byteArray.length;
byte[] result = new byte[length];
System.arraycopy(this.byteArray, 0, result, 0, length);
return result;
}
@Override
public String getContentAsString(Charset charset) throws IOException {
return new String(this.byteArray, charset);
}
/**
* This implementation returns a description that includes the passed-in
* {@code description}, if any.

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* Copyright 2002-2023 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.
@@ -27,6 +27,7 @@ import java.net.URL;
import java.nio.channels.FileChannel;
import java.nio.channels.ReadableByteChannel;
import java.nio.channels.WritableByteChannel;
import java.nio.charset.Charset;
import java.nio.file.FileSystem;
import java.nio.file.Files;
import java.nio.file.NoSuchFileException;
@@ -196,6 +197,26 @@ public class FileSystemResource extends AbstractResource implements WritableReso
}
}
@Override
public byte[] getContentAsByteArray() throws IOException {
try {
return Files.readAllBytes(this.filePath);
}
catch (NoSuchFileException ex) {
throw new FileNotFoundException(ex.getMessage());
}
}
@Override
public String getContentAsString(Charset charset) throws IOException {
try {
return Files.readString(this.filePath, charset);
}
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).

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 the original author or authors.
* Copyright 2002-2023 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.
@@ -25,6 +25,7 @@ import java.net.URI;
import java.net.URL;
import java.nio.channels.ReadableByteChannel;
import java.nio.channels.WritableByteChannel;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.NoSuchFileException;
import java.nio.file.OpenOption;
@@ -141,6 +142,26 @@ public class PathResource extends AbstractResource implements WritableResource {
return Files.newInputStream(this.path);
}
@Override
public byte[] getContentAsByteArray() throws IOException {
try {
return Files.readAllBytes(this.path);
}
catch (NoSuchFileException ex) {
throw new FileNotFoundException(ex.getMessage());
}
}
@Override
public String getContentAsString(Charset charset) throws IOException {
try {
return Files.readString(this.path, charset);
}
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).

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* Copyright 2002-2023 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.
@@ -17,9 +17,9 @@
package org.springframework.core.io;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URI;
import java.net.URL;
import java.nio.channels.Channels;
@@ -27,6 +27,7 @@ import java.nio.channels.ReadableByteChannel;
import java.nio.charset.Charset;
import org.springframework.lang.Nullable;
import org.springframework.util.FileCopyUtils;
/**
* Interface for a resource descriptor that abstracts from the actual
@@ -179,40 +180,29 @@ public interface Resource extends InputStreamSource {
String getDescription();
/**
* Return a {@link ReadableByteChannel}.
* <p>It is expected that each call creates a <i>fresh</i> channel.
* <p>The default implementation returns {@link Channels#newChannel(InputStream)}
* with the result of {@link #getInputStream()}.
* @return the byte channel for the underlying resource (must not be {@code null})
* @throws java.io.FileNotFoundException if the underlying resource doesn't exist
* @throws IOException if the content channel could not be opened
* @since 5.0
* @see #getInputStream()
* Return the contents of this resource as a byte array.
* @return the contents of this resource as byte array
* @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
* @throws IOException in case of general resolution/reading failures
* @since 6.0.5
*/
/**
* Returns the contents of a file as a string using the system default Charset.
* <p>The default implementation returns a {@link Object#toString()} representation of the resource.
* @return the contents of the requested file as a {@code String}.
* @throws FileNotFoundException in the event the file path is invalid.
* @throws IOException if the file can not be read or cannot be accessed.
* @since 5.2.5
*/
default String getContentAsString() throws IOException{
return toString();
default byte[] getContentAsByteArray() throws IOException {
return FileCopyUtils.copyToByteArray(getInputStream());
}
/**
* Returns the contents of a file as a string using the specified Charset.
* <p>The default implementation returns a {@link Object#toString()} representation of the resource.
* @param charset the {@code Charset} to use to deserialize the content. Defaults to system default.
* @return the contents of the requested file as a {@code String}.
* @throws FileNotFoundException in the event the file path is invalid.
* @throws IOException if the file can not be read or cannot be accessed.
* @since 5.2.5
* Returns the contents of this resource as a string, using the specified
* charset.
* @param charset the charset to use for decoding
* @return the contents of this resource as a {@code String}
* @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
* @throws IOException in case of general resolution/reading failures
* @since 6.0.5
*/
default String getContentAsString(Charset charset) throws IOException{
return toString();
default String getContentAsString(Charset charset) throws IOException {
return FileCopyUtils.copyToString(new InputStreamReader(getInputStream(), charset));
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2021 the original author or authors.
* Copyright 2002-2023 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.
@@ -159,6 +159,27 @@ public class EncodedResource implements InputStreamSource {
return this.resource.getInputStream();
}
/**
* Returns the contents of the specified resource as a string, using the specified
* {@link #getCharset() Charset} or {@linkplain #getEncoding() encoding}
* (if any).
* @throws IOException if opening the resource failed
* @since 6.0.5
*/
public String getContentAsString() throws IOException {
Charset charset;
if (this.charset != null) {
charset = this.charset;
}
else if (this.encoding != null) {
charset = Charset.forName(this.encoding);
}
else {
charset = Charset.defaultCharset();
}
return this.resource.getContentAsString(charset);
}
@Override
public boolean equals(@Nullable Object other) {