我想使用Google提供的Java Library com.google.cloud.storage从Google Cloud Storage下载大文件。我有工作代码,但我仍然有一个问题和一个主要问题:
我的主要担心是,文件内容实际下载了什么时候?在(引用下面的代码)storage.get(blobId)
期间,在blob.reader()
期间还是在reader.read(bytes)
期间?当涉及如何处理无效的校验和时,这变得非常重要,我需要做什么才能实际触发该文件再次通过网络获取?
更简单的问题是:是否内置功能可以进行MD5(或CRC32C)检查Google库中收到的文件?也许我不需要自己实施。
这是我尝试从Google云存储下载大文件的方法:
private static final int MAX_NUMBER_OF_TRIES = 3;
public Path downloadFile(String storageFileName, String bucketName) throws IOException {
// In my real code, this is a field populated in the constructor.
Storage storage = Objects.requireNonNull(StorageOptions.getDefaultInstance().getService());
BlobId blobId = BlobId.of(bucketName, storageFileName);
Path outputFile = Paths.get(storageFileName.replaceAll("/", "-"));
int retryCounter = 1;
Blob blob;
boolean checksumOk;
MessageDigest messageDigest;
try {
messageDigest = MessageDigest.getInstance("MD5");
} catch (NoSuchAlgorithmException ex) {
throw new RuntimeException(ex);
}
do {
LOGGER.debug("Start download file {} from bucket {} to Content Store (try {})", storageFileName, bucketName, retryCounter);
blob = storage.get(blobId);
if (null == blob) {
throw new CloudStorageCommunicationException("Failed to download file after " + retryCounter + " tries.");
}
if (Files.exists(outputFile)) {
Files.delete(outputFile);
}
try (ReadChannel reader = blob.reader();
FileChannel channel = new FileOutputStream(outputFile.toFile(), true).getChannel()) {
ByteBuffer bytes = ByteBuffer.allocate(128 * 1024);
int bytesRead = reader.read(bytes);
while (bytesRead > 0) {
bytes.flip();
messageDigest.update(bytes.array(), 0, bytesRead);
channel.write(bytes);
bytes.clear();
bytesRead = reader.read(bytes);
}
}
String checksum = Base64.encodeBase64String(messageDigest.digest());
checksumOk = checksum.equals(blob.getMd5());
if (!checksumOk) {
Files.delete(outputFile);
messageDigest.reset();
}
} while (++retryCounter <= MAX_NUMBER_OF_TRIES && !checksumOk);
if (!checksumOk) {
throw new CloudStorageCommunicationException("Failed to download file after " + MAX_NUMBER_OF_TRIES + " tries.");
}
return outputFile;
}
Google-cloud-Java存储库在读取超过正常HTTPS/TCP正确性检查的数据时不会单独验证校验和。如果它将接收到的数据的MD5与已知的MD5进行了比较,则需要下载整个文件,然后才能返回read()
的任何结果,对于非常大的文件而言,这是不可行的。
如果您需要对MD5进行比较的额外保护,那么您正在做的是一个好主意。如果这是一次性任务,则可以使用gsutil
命令行工具,该工具执行相同的其他检查。
正如 ReadChannel
的javadoc所说:
该类的实现可能会在内部缓冲数据以减少远程调用。
因此,您从blob.reader()
获得的实现可以缓存整个文件,某些字节或什么都没有,然后在您调用read()
时获取字节。你永远不会知道,你不应该在乎。
只有read()
抛出IOException
,而您使用的其他方法没有,我会说只有调用read()
实际上会下载内容。您还可以在Lib的来源中看到这一点。
顺便说一句。尽管在库的Javadocs中有一个示例,但您应该检查>= 0
而不是> 0
。0
只是意味着什么都没有阅读,而不是到达流的末端。流的结尾是通过返回-1
的信号。
要在检查失败检查后重试,请从Blob中获取新读者。如果某些东西可以缓存下载的数据,则可以读者本身。因此,如果您从Blob中获得新读者,则文件将从远程重新下载。