Skip to content

Commit 34e797d

Browse files
authored
samples: add samples for ranged reads and appendable uploads (#3275)
1 parent c3b05ac commit 34e797d

File tree

6 files changed

+411
-0
lines changed

6 files changed

+411
-0
lines changed
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
/*
2+
* Copyright 2025 Google LLC
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.example.storage.object;
18+
19+
// [START storage_read_appendable_object_multiple_ranges]
20+
21+
import com.google.api.core.ApiFuture;
22+
import com.google.api.core.ApiFutures;
23+
import com.google.cloud.storage.BlobId;
24+
import com.google.cloud.storage.BlobReadSession;
25+
import com.google.cloud.storage.RangeSpec;
26+
import com.google.cloud.storage.ReadProjectionConfigs;
27+
import com.google.cloud.storage.Storage;
28+
import com.google.cloud.storage.StorageOptions;
29+
import com.google.common.collect.ImmutableList;
30+
import java.util.List;
31+
import java.util.concurrent.TimeUnit;
32+
33+
public class AppendableObjectMultipleRangedRead {
34+
public static void appendableObjectMultipleRangedRead(
35+
String bucketName, String objectName, long offset1, int length1, long offset2, int length2)
36+
throws Exception {
37+
try (Storage storage = StorageOptions.grpc().build().getService()) {
38+
BlobId blobId = BlobId.of(bucketName, objectName);
39+
ApiFuture<BlobReadSession> futureBlobReadSession = storage.blobReadSession(blobId);
40+
RangeSpec rangeSpec1 = RangeSpec.of(offset1, length1);
41+
RangeSpec rangeSpec2 = RangeSpec.of(offset2, length2);
42+
43+
try (BlobReadSession blobReadSession = futureBlobReadSession.get(10, TimeUnit.SECONDS)) {
44+
ApiFuture<byte[]> future1 =
45+
blobReadSession.readAs(ReadProjectionConfigs.asFutureBytes().withRangeSpec(rangeSpec1));
46+
ApiFuture<byte[]> future2 =
47+
blobReadSession.readAs(ReadProjectionConfigs.asFutureBytes().withRangeSpec(rangeSpec2));
48+
49+
List<byte[]> allBytes = ApiFutures.allAsList(ImmutableList.of(future1, future2)).get();
50+
51+
byte[] bytes1 = allBytes.get(0);
52+
byte[] bytes2 = allBytes.get(1);
53+
54+
System.out.println(
55+
"Successfully read "
56+
+ bytes1.length
57+
+ " bytes from range 1 and "
58+
+ bytes2.length
59+
+ " bytes from range 2.");
60+
}
61+
}
62+
}
63+
}
64+
65+
// [END storage_read_appendable_object_multiple_ranges]
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
/*
2+
* Copyright 2025 Google LLC
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.example.storage.object;
18+
19+
// [START storage_read_appendable_object_full]
20+
21+
import com.google.api.core.ApiFuture;
22+
import com.google.cloud.storage.BlobId;
23+
import com.google.cloud.storage.BlobReadSession;
24+
import com.google.cloud.storage.ReadAsChannel;
25+
import com.google.cloud.storage.ReadProjectionConfigs;
26+
import com.google.cloud.storage.Storage;
27+
import com.google.cloud.storage.StorageOptions;
28+
import java.nio.ByteBuffer;
29+
import java.nio.channels.ScatteringByteChannel;
30+
import java.util.Locale;
31+
import java.util.concurrent.TimeUnit;
32+
33+
public class AppendableObjectReadFullObject {
34+
public static void appendableObjectReadFullObject(String bucketName, String objectName)
35+
throws Exception {
36+
try (Storage storage = StorageOptions.grpc().build().getService()) {
37+
BlobId blobId = BlobId.of(bucketName, objectName);
38+
ApiFuture<BlobReadSession> futureBlobReadSession = storage.blobReadSession(blobId);
39+
40+
try (BlobReadSession blobReadSession = futureBlobReadSession.get(10, TimeUnit.SECONDS)) {
41+
42+
ReadAsChannel readAsChannelConfig = ReadProjectionConfigs.asChannel();
43+
try (ScatteringByteChannel channel = blobReadSession.readAs(readAsChannelConfig)) {
44+
long totalBytesRead = 0;
45+
ByteBuffer buffer = ByteBuffer.allocate(64 * 1024);
46+
int bytesRead;
47+
48+
while ((bytesRead = channel.read(buffer)) != -1) {
49+
totalBytesRead += bytesRead;
50+
buffer.clear();
51+
}
52+
53+
System.out.printf(
54+
Locale.US,
55+
"Successfully read a total of %d bytes from object %s%n",
56+
totalBytesRead,
57+
blobId.toGsUtilUri());
58+
}
59+
}
60+
}
61+
}
62+
}
63+
// [END storage_read_appendable_object_full]
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
/*
2+
* Copyright 2025 Google LLC
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.example.storage.object;
18+
19+
// [START storage_read_appendable_object_single_range]
20+
21+
import com.google.api.core.ApiFuture;
22+
import com.google.cloud.storage.BlobId;
23+
import com.google.cloud.storage.BlobReadSession;
24+
import com.google.cloud.storage.RangeSpec;
25+
import com.google.cloud.storage.ReadProjectionConfigs;
26+
import com.google.cloud.storage.Storage;
27+
import com.google.cloud.storage.StorageOptions;
28+
import java.util.concurrent.TimeUnit;
29+
30+
public class AppendableObjectSingleRangedRead {
31+
public static void appendableObjectSingleRangedRead(
32+
String bucketName, String objectName, long offset, int length) throws Exception {
33+
34+
try (Storage storage = StorageOptions.grpc().build().getService()) {
35+
BlobId blobId = BlobId.of(bucketName, objectName);
36+
ApiFuture<BlobReadSession> futureBlobReadSession = storage.blobReadSession(blobId);
37+
38+
try (BlobReadSession blobReadSession = futureBlobReadSession.get(10, TimeUnit.SECONDS)) {
39+
// Define the range of bytes to read.
40+
RangeSpec rangeSpec = RangeSpec.of(offset, length);
41+
ApiFuture<byte[]> future =
42+
blobReadSession.readAs(ReadProjectionConfigs.asFutureBytes().withRangeSpec(rangeSpec));
43+
44+
// Wait for the read to complete.
45+
byte[] bytes = future.get();
46+
47+
System.out.println(
48+
"Successfully read "
49+
+ bytes.length
50+
+ " bytes from object "
51+
+ objectName
52+
+ " in bucket "
53+
+ bucketName);
54+
}
55+
}
56+
}
57+
}
58+
// [END storage_read_appendable_object_single_range]
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
/*
2+
* Copyright 2025 Google LLC
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.example.storage.object;
18+
19+
// [START storage_finalize_appendable_object_upload]
20+
21+
import com.google.cloud.storage.Blob;
22+
import com.google.cloud.storage.BlobAppendableUpload;
23+
import com.google.cloud.storage.BlobAppendableUploadConfig;
24+
import com.google.cloud.storage.BlobId;
25+
import com.google.cloud.storage.BlobInfo;
26+
import com.google.cloud.storage.Storage;
27+
import com.google.cloud.storage.StorageOptions;
28+
29+
public class FinalizeAppendableObjectUpload {
30+
public static void finalizeAppendableObjectUpload(String bucketName, String objectName)
31+
throws Exception {
32+
// The ID of your GCS bucket
33+
// String bucketName = "your-unique-bucket-name";
34+
35+
// The ID of your GCS unfinalized appendable object
36+
// String objectName = "your-object-name";
37+
38+
try (Storage storage = StorageOptions.grpc().build().getService()) {
39+
BlobId blobId = BlobId.of(bucketName, objectName);
40+
Blob existingBlob = storage.get(blobId);
41+
42+
if (existingBlob == null) {
43+
System.out.println("Object " + objectName + " not found in bucket " + bucketName);
44+
return;
45+
}
46+
47+
BlobInfo blobInfoForTakeover = BlobInfo.newBuilder(existingBlob.getBlobId()).build();
48+
BlobAppendableUpload finalizingSession =
49+
storage.blobAppendableUpload(
50+
blobInfoForTakeover,
51+
BlobAppendableUploadConfig.of()
52+
.withCloseAction(BlobAppendableUploadConfig.CloseAction.FINALIZE_WHEN_CLOSING));
53+
54+
try (BlobAppendableUpload.AppendableUploadWriteableByteChannel channel =
55+
finalizingSession.open()) {
56+
channel.finalizeAndClose();
57+
}
58+
59+
System.out.println(
60+
"Successfully finalized object " + objectName + " in bucket " + bucketName);
61+
}
62+
}
63+
}
64+
// [END storage_finalize_appendable_object_upload]
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
/*
2+
* Copyright 2025 Google LLC
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.example.storage.object;
18+
19+
// [START storage_resume_appendable_object_upload]
20+
21+
import com.google.cloud.storage.Blob;
22+
import com.google.cloud.storage.BlobAppendableUpload;
23+
import com.google.cloud.storage.BlobAppendableUpload.AppendableUploadWriteableByteChannel;
24+
import com.google.cloud.storage.BlobAppendableUploadConfig;
25+
import com.google.cloud.storage.BlobAppendableUploadConfig.CloseAction;
26+
import com.google.cloud.storage.BlobId;
27+
import com.google.cloud.storage.BlobInfo;
28+
import com.google.cloud.storage.Storage;
29+
import com.google.cloud.storage.StorageOptions;
30+
import com.google.common.io.ByteStreams;
31+
import java.io.IOException;
32+
import java.nio.channels.FileChannel;
33+
import java.nio.file.Paths;
34+
import java.util.Locale;
35+
36+
public class ResumeAppendableObjectUpload {
37+
public static void resumeAppendableObjectUpload(
38+
String bucketName, String objectName, String filePath) throws Exception {
39+
// The ID of your GCS bucket
40+
// String bucketName = "your-unique-bucket-name";
41+
42+
// The ID of your GCS unfinalized appendable object
43+
// String objectName = "your-object-name";
44+
45+
// The path to the file to upload
46+
// String filePath = "path/to/your/file";
47+
48+
try (Storage storage = StorageOptions.grpc().build().getService()) {
49+
BlobId blobId = BlobId.of(bucketName, objectName);
50+
Blob existingBlob = storage.get(blobId);
51+
BlobInfo blobInfoForTakeover = BlobInfo.newBuilder(existingBlob.getBlobId()).build();
52+
53+
long currentObjectSize = existingBlob.getSize();
54+
System.out.printf(
55+
Locale.US,
56+
"Resuming upload for %s. Currently uploaded size: %d bytes\n",
57+
blobId.toGsUtilUri(),
58+
currentObjectSize);
59+
60+
BlobAppendableUploadConfig config =
61+
BlobAppendableUploadConfig.of().withCloseAction(CloseAction.CLOSE_WITHOUT_FINALIZING);
62+
BlobAppendableUpload resumeUploadSession =
63+
storage.blobAppendableUpload(blobInfoForTakeover, config);
64+
try (FileChannel fileChannel = FileChannel.open(Paths.get(filePath));
65+
AppendableUploadWriteableByteChannel channel = resumeUploadSession.open()) {
66+
67+
if (fileChannel.size() < currentObjectSize) {
68+
throw new IOException(
69+
"Local file is smaller than the already uploaded data. File size: "
70+
+ fileChannel.size()
71+
+ ", Uploaded size: "
72+
+ currentObjectSize);
73+
} else if (fileChannel.size() == currentObjectSize) {
74+
System.out.println("No more data to upload.");
75+
} else {
76+
fileChannel.position(currentObjectSize);
77+
System.out.printf(
78+
Locale.US, "Appending %d bytes\n", fileChannel.size() - currentObjectSize);
79+
ByteStreams.copy(fileChannel, channel);
80+
}
81+
}
82+
BlobInfo result = storage.get(blobId);
83+
System.out.printf(
84+
Locale.US,
85+
"Object %s successfully resumed. Total size: %d\n",
86+
result.getBlobId().toGsUtilUriWithGeneration(),
87+
result.getSize());
88+
}
89+
}
90+
}
91+
// [END storage_resume_appendable_object_upload]

0 commit comments

Comments
 (0)