我正尝试使用他们的Google Cloud Storage从Java SDK下载一个字节范围。
我可以这样下载整个文件。
Storage mStorage; // initialized and working
Blob blob = mStorage.get(pBucketName, pSource);
try (ReadChannel reader = mStorage.reader(blob.getBlobId())) {
// read bytes from read channel
}
如果需要,我可以ReadChannel#seek(long)
直到到达所需的起始字节,然后从该点下载一个范围,但这似乎效率不高(尽管我不确切知道实现中发生了什么。)< / p>
理想情况下,我想将Range: bytes=start-end
标头指定为shown in the Google Cloud Storage REST API,但是我不知道如何在Java中设置标头。
如何在Java SDK Storage get调用中指定字节范围或指定标头,以便有效地下载所需的字节范围?
答案 0 :(得分:1)
我了解您正在尝试使用Google Cloud的特定界面,但是您可能不知道还有另一种方式:Google Cloud可以插入Java的NIO界面。您可以将Path
获取到存储桶中的文件,然后按常规使用它:将SeekableChannel插入文件,然后调用position(long)
方法以获取要读取的位置。
这是我测试过的示例代码:
import java.io.IOException;
import java.net.URI;
import java.nio.ByteBuffer;
import java.nio.channels.SeekableByteChannel;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
(...)
public static void readFromMiddle(String path, long offset, ByteBuffer buf) throws IOException {
// Convert from a string to a path, using available NIO providers
// so paths like gs://bucket/file are recognized (provided you included the google-cloud-nio
// dependency).
Path p = Paths.get(URI.create(path));
SeekableByteChannel chan = Files.newByteChannel(p, StandardOpenOption.READ);
chan.position(offset);
chan.read(buf);
}
您将认识到这是普通的Java代码,除了我们制作Path
的不同寻常方式之外,没有什么特别的。那就是蔚来的美丽。为了使此代码能够理解“ gs://” URL,您需要添加google-cloud-nio依赖项。对于Maven,就像这样:
<dependency>
<groupId>com.google.cloud</groupId>
<artifactId>google-cloud-nio</artifactId>
<version>0.107.0-alpha</version>
</dependency>
仅此而已。
The documentation page显示了如何对其他依赖项管理器进行操作,并提供了一些其他信息。
答案 1 :(得分:1)
解决方案是仅调用ReadChannel#seek(offset)
。
例如:
try (ReadChannel reader = blob.reader()) {
// offset and readLength is obtained from HTTP Range Header
reader.seek(offset);
ByteBuffer bytes = ByteBuffer.allocate(1 * 1024 * 1024);
int len = 0;
while ((len = reader.read(bytes)) > 0 && readLength > 0) {
outputStream.write(bytes.array(), 0, (int) Math.min(len, readLength));
bytes.clear();
readLength -= len;
}
}
答案 2 :(得分:0)
事实证明,您无法在当前 Java SDK 实现中对范围标头进行细粒度控制。
您可以通过 ReadChannel#seek(offset)
设置起始位置,但不能设置结束位置。
在 Java SDK 内部,它会将范围标头设置为 Rrange:$offset-$(offset+bufferSize)
。
解决方法是自己包装 ReadChannel
,并在它到达预期的结束位置时关闭连接。代码片段:
class GSBlobInputStream extends InputStream {
private final ReadChannel channel;
private long start = 0;
private long end = -1;
private InputStream delegate;
public GSBlobInputStream(ReadChannel channel) {
this.channel = channel;
}
public GSBlobInputStream(ReadChannel channel, long start, long end) {
this.channel = channel;
this.start = start;
this.end = end;
}
@Override
public int read() throws IOException {
init();
return delegate.read();
}
@Override
public int read(byte[] b) throws IOException {
init();
return delegate.read(b);
}
@Override
public int read(byte[] b, int off, int len) throws IOException {
init();
return delegate.read(b, off, len);
}
/**
* Closes this input stream and releases any system resources associated with the stream.
*
* @throws IOException if an I/O error occurs.
*/
@Override
public void close() throws IOException {
if (delegate != null) {
delegate.close();
}
}
private void init() throws IOException {
if (delegate != null) {
return;
}
channel.seek(start);
delegate = Channels.newInputStream(channel);
if (end != -1) {
delegate = ByteStreams.limit(delegate, end - start + 1);
}
}
}
请注意,在这种方法中,在最坏的情况下您将有 15MiB - 1 字节的开销,因为默认缓冲区大小为 15MiB。
答案 3 :(得分:-1)
这是一个读取对象内容的好例子。 在此链接中,有更多代码解决方案:
Stream file from Google Cloud Storage
/**
* Example of reading a blob's content through a reader.
*/
// [TARGET reader(String, String, BlobSourceOption...)]
// [VARIABLE "my_unique_bucket"]
// [VARIABLE "my_blob_name"]
public void readerFromStrings(String bucketName, String blobName) throws IOException {
// [START readerFromStrings]
try (ReadChannel reader = storage.reader(bucketName, blobName)) {
ByteBuffer bytes = ByteBuffer.allocate(64 * 1024);
while (reader.read(bytes) > 0) {
bytes.flip();
// do something with bytes
bytes.clear();
}
}
// [END readerFromStrings]
}