Skip to content

Commit b467331

Browse files
authored
samples(feat): add storage_move_object sample (#3191)
Rename existing MoveObject class to CopyDeleteObject to make it clear there is a copy and delete happening, not a move. (sample name is left `storage_move_file`).
1 parent bf5c1ee commit b467331

File tree

3 files changed

+104
-10
lines changed

3 files changed

+104
-10
lines changed
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
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_move_object]
20+
21+
import com.google.cloud.storage.BlobId;
22+
import com.google.cloud.storage.BlobInfo;
23+
import com.google.cloud.storage.Storage;
24+
import com.google.cloud.storage.Storage.MoveBlobRequest;
25+
import com.google.cloud.storage.StorageOptions;
26+
27+
public final class AtomicMoveObject {
28+
29+
public static void moveObject(
30+
String projectId, String bucketName, String sourceObjectName, String targetObjectName) {
31+
32+
// The ID of your GCP project
33+
// String projectId = "your-project-id";
34+
35+
// The ID of your GCS bucket
36+
// String bucketName = "your-unique-bucket-name";
37+
38+
// The ID of your GCS object
39+
// String sourceObjectName = "your-object-name";
40+
41+
// The ID of your GCS object
42+
// String targetObjectName = "your-new-object-name";
43+
44+
Storage storage = StorageOptions.newBuilder().setProjectId(projectId).build().getService();
45+
BlobId source = BlobId.of(bucketName, sourceObjectName);
46+
BlobId target = BlobId.of(bucketName, targetObjectName);
47+
48+
// Optional: set a generation-match precondition to avoid potential race
49+
// conditions and data corruptions. The request returns a 412 error if the
50+
// preconditions are not met.
51+
Storage.BlobTargetOption precondition;
52+
BlobInfo existingTarget = storage.get(target);
53+
if (existingTarget == null) {
54+
// For a target object that does not yet exist, set the DoesNotExist precondition.
55+
// This will cause the request to fail if the object is created before the request runs.
56+
precondition = Storage.BlobTargetOption.doesNotExist();
57+
} else {
58+
// If the destination already exists in your bucket, instead set a generation-match
59+
// precondition. This will cause the request to fail if the existing object's generation
60+
// changes before the request runs.
61+
precondition = Storage.BlobTargetOption.generationMatch(existingTarget.getGeneration());
62+
}
63+
64+
// Atomically move source object to target object within the bucket
65+
MoveBlobRequest moveBlobRequest =
66+
MoveBlobRequest.newBuilder()
67+
.setSource(source)
68+
.setTarget(target)
69+
.setTargetOptions(precondition)
70+
.build();
71+
BlobInfo movedBlob = storage.moveBlob(moveBlobRequest);
72+
73+
System.out.println(
74+
"Moved object "
75+
+ source.toGsUtilUri()
76+
+ " to "
77+
+ movedBlob.getBlobId().toGsUtilUriWithGeneration());
78+
}
79+
}
80+
// [END storage_move_object]

samples/snippets/src/main/java/com/example/storage/object/MoveObject.java renamed to samples/snippets/src/main/java/com/example/storage/object/CopyDeleteObject.java

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,12 @@
2020

2121
import com.google.cloud.storage.Blob;
2222
import com.google.cloud.storage.BlobId;
23+
import com.google.cloud.storage.BlobInfo;
2324
import com.google.cloud.storage.Storage;
2425
import com.google.cloud.storage.StorageOptions;
2526

26-
public class MoveObject {
27-
public static void moveObject(
27+
public class CopyDeleteObject {
28+
public static void copyDeleteObject(
2829
String projectId,
2930
String sourceBucketName,
3031
String sourceObjectName,
@@ -34,7 +35,7 @@ public static void moveObject(
3435
// String projectId = "your-project-id";
3536

3637
// The ID of your GCS bucket
37-
// String bucketName = "your-unique-bucket-name";
38+
// String sourceBucketName = "your-unique-bucket-name";
3839

3940
// The ID of your GCS object
4041
// String sourceObjectName = "your-object-name";
@@ -53,17 +54,16 @@ public static void moveObject(
5354
// conditions and data corruptions. The request returns a 412 error if the
5455
// preconditions are not met.
5556
Storage.BlobTargetOption precondition;
56-
if (storage.get(targetBucketName, targetObjectName) == null) {
57+
BlobInfo existingTarget = storage.get(targetBucketName, targetObjectName);
58+
if (existingTarget == null) {
5759
// For a target object that does not yet exist, set the DoesNotExist precondition.
5860
// This will cause the request to fail if the object is created before the request runs.
5961
precondition = Storage.BlobTargetOption.doesNotExist();
6062
} else {
6163
// If the destination already exists in your bucket, instead set a generation-match
6264
// precondition. This will cause the request to fail if the existing object's generation
6365
// changes before the request runs.
64-
precondition =
65-
Storage.BlobTargetOption.generationMatch(
66-
storage.get(targetBucketName, targetObjectName).getGeneration());
66+
precondition = Storage.BlobTargetOption.generationMatch(existingTarget.getGeneration());
6767
}
6868

6969
// Copy source object to target object

samples/snippets/src/test/java/com/example/storage/ITObjectSnippets.java

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,10 +26,12 @@
2626
import static org.junit.Assert.assertNull;
2727
import static org.junit.Assert.assertTrue;
2828

29+
import com.example.storage.object.AtomicMoveObject;
2930
import com.example.storage.object.BatchSetObjectMetadata;
3031
import com.example.storage.object.ChangeObjectCsekToKms;
3132
import com.example.storage.object.ChangeObjectStorageClass;
3233
import com.example.storage.object.ComposeObject;
34+
import com.example.storage.object.CopyDeleteObject;
3335
import com.example.storage.object.CopyObject;
3436
import com.example.storage.object.CopyOldVersionOfObject;
3537
import com.example.storage.object.DeleteObject;
@@ -48,7 +50,6 @@
4850
import com.example.storage.object.ListSoftDeletedObjects;
4951
import com.example.storage.object.ListSoftDeletedVersionsOfObject;
5052
import com.example.storage.object.MakeObjectPublic;
51-
import com.example.storage.object.MoveObject;
5253
import com.example.storage.object.RestoreSoftDeletedObject;
5354
import com.example.storage.object.RotateObjectEncryptionKey;
5455
import com.example.storage.object.SetObjectMetadata;
@@ -258,7 +259,7 @@ public void testListObjectsWithPrefix() {
258259
}
259260

260261
@Test
261-
public void testMoveObject() throws Exception {
262+
public void testCopyDeleteObject() throws Exception {
262263
String blob = generator.randomObjectName();
263264
String newBlob = generator.randomObjectName();
264265

@@ -270,12 +271,25 @@ public void testMoveObject() throws Exception {
270271

271272
String newBucket = tmpBucket.getBucket().getName();
272273
BlobInfo gen1 = storage.create(BlobInfo.newBuilder(BlobId.of(newBucket, blob)).build());
273-
MoveObject.moveObject(GOOGLE_CLOUD_PROJECT, newBucket, blob, newBucket, newBlob);
274+
CopyDeleteObject.copyDeleteObject(GOOGLE_CLOUD_PROJECT, newBucket, blob, newBucket, newBlob);
274275
assertNotNull(storage.get(newBucket, newBlob));
275276
assertNull(storage.get(bucket.getName(), blob));
276277
}
277278
}
278279

280+
@Test
281+
public void testAtomicMoveObject() {
282+
String blob1 = generator.randomObjectName();
283+
String blob2 = generator.randomObjectName();
284+
285+
String bucketName = bucket.getName();
286+
BlobInfo gen1 = storage.create(BlobInfo.newBuilder(BlobId.of(bucketName, blob1)).build());
287+
AtomicMoveObject.moveObject(GOOGLE_CLOUD_PROJECT, bucketName, blob1, blob2);
288+
assertThat(storage.get(bucketName, blob1)).isNull();
289+
assertThat(storage.get(bucketName, blob2)).isNotNull();
290+
assertThat(stdOut.getCapturedOutputAsUtf8String()).contains("Moved object");
291+
}
292+
279293
@Test
280294
public void testSetObjectMetadata() {
281295
String bucketName = bucket.getName();

0 commit comments

Comments
 (0)