Use the same file handle in file system getBlob

Previously getBlob lazily returned a ByteSource which could return a
different content length than the blob metadata.  Instead eagerly open
a FileInputStream for consistency.  Note that this has some
consequences for LocalBlobStore.getBlob when used with ranges.
This commit is contained in:
Andrew Gaul 2023-01-22 13:17:07 +09:00
parent b7f28f1e6a
commit aae0844cdf

View File

@ -36,7 +36,9 @@ import static org.jclouds.filesystem.util.Utils.setPrivate;
import static org.jclouds.filesystem.util.Utils.setPublic; import static org.jclouds.filesystem.util.Utils.setPublic;
import static org.jclouds.util.Closeables2.closeQuietly; import static org.jclouds.util.Closeables2.closeQuietly;
import java.io.ByteArrayInputStream;
import java.io.File; import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException; import java.io.FileNotFoundException;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
@ -377,8 +379,10 @@ public class FilesystemStorageStrategyImpl implements LocalStorageStrategy {
BlobBuilder builder = blobBuilders.get(); BlobBuilder builder = blobBuilders.get();
builder.name(key); builder.name(key);
File file = getFileForBlobKey(container, key); File file = getFileForBlobKey(container, key);
InputStream is;
ByteSource byteSource; ByteSource byteSource;
boolean isDirectory = false; boolean isDirectory = false;
long length;
if (getDirectoryBlobSuffix(key) != null) { if (getDirectoryBlobSuffix(key) != null) {
if (!file.isDirectory()) { if (!file.isDirectory()) {
@ -391,8 +395,20 @@ public class FilesystemStorageStrategyImpl implements LocalStorageStrategy {
logger.debug("%s - %s is a directory", container, key); logger.debug("%s - %s is a directory", container, key);
byteSource = ByteSource.empty(); byteSource = ByteSource.empty();
isDirectory = true; isDirectory = true;
length = 0;
is = new ByteArrayInputStream(new byte[0]);
} else { } else {
byteSource = Files.asByteSource(file); byteSource = Files.asByteSource(file);
try {
// Must operate only an open file handle to avoid concurrent putBlob changing the object
FileInputStream fis = new FileInputStream(file);
length = fis.getChannel().size();
is = fis;
} catch (FileNotFoundException fnfe) {
return null;
} catch (Exception e) {
throw new RuntimeException(e);
}
} }
try { try {
String cacheControl = null; String cacheControl = null;
@ -406,6 +422,7 @@ public class FilesystemStorageStrategyImpl implements LocalStorageStrategy {
Tier tier = Tier.STANDARD; Tier tier = Tier.STANDARD;
ImmutableMap.Builder<String, String> userMetadata = ImmutableMap.builder(); ImmutableMap.Builder<String, String> userMetadata = ImmutableMap.builder();
// TODO: not atomic -- how to read from the FileChannel?
UserDefinedFileAttributeView view = getUserDefinedFileAttributeView(file.toPath()); UserDefinedFileAttributeView view = getUserDefinedFileAttributeView(file.toPath());
if (view != null) { if (view != null) {
try { try {
@ -455,12 +472,12 @@ public class FilesystemStorageStrategyImpl implements LocalStorageStrategy {
logger.debug("xattrs not supported on %s", file.toPath()); logger.debug("xattrs not supported on %s", file.toPath());
} }
builder.payload(byteSource) builder.payload(is)
.cacheControl(cacheControl) .cacheControl(cacheControl)
.contentDisposition(contentDisposition) .contentDisposition(contentDisposition)
.contentEncoding(contentEncoding) .contentEncoding(contentEncoding)
.contentLanguage(contentLanguage) .contentLanguage(contentLanguage)
.contentLength(byteSource.size()) .contentLength(length)
.contentMD5(hashCode) .contentMD5(hashCode)
.eTag(eTag) .eTag(eTag)
.contentType(contentType) .contentType(contentType)
@ -469,8 +486,9 @@ public class FilesystemStorageStrategyImpl implements LocalStorageStrategy {
.type(isDirectory ? StorageType.FOLDER : StorageType.BLOB) .type(isDirectory ? StorageType.FOLDER : StorageType.BLOB)
.userMetadata(userMetadata.build()); .userMetadata(userMetadata.build());
} else { } else {
builder.payload(byteSource) builder.payload(is)
.contentLength(byteSource.size()) .contentLength(length)
// TODO: not atomic and expensive for large objects
.contentMD5(byteSource.hash(Hashing.md5()).asBytes()); .contentMD5(byteSource.hash(Hashing.md5()).asBytes());
} }
} catch (FileNotFoundException fnfe) { } catch (FileNotFoundException fnfe) {
@ -480,8 +498,9 @@ public class FilesystemStorageStrategyImpl implements LocalStorageStrategy {
} }
Blob blob = builder.build(); Blob blob = builder.build();
blob.getMetadata().setContainer(container); blob.getMetadata().setContainer(container);
// TODO: not atomic
blob.getMetadata().setLastModified(new Date(file.lastModified())); blob.getMetadata().setLastModified(new Date(file.lastModified()));
blob.getMetadata().setSize(file.length()); blob.getMetadata().setSize(length);
if (blob.getPayload().getContentMetadata().getContentMD5() != null) if (blob.getPayload().getContentMetadata().getContentMD5() != null)
blob.getMetadata().setETag(base16().lowerCase().encode(blob.getPayload().getContentMetadata().getContentMD5())); blob.getMetadata().setETag(base16().lowerCase().encode(blob.getPayload().getContentMetadata().getContentMD5()));
return blob; return blob;