4646/**
4747 * An example of using the Google Cloud Storage.
4848 * <p>
49- * This example demonstrates a simple/typical usage.
49+ * This example demonstrates a simple/typical storage usage.
5050 * <p>
5151 * Steps needed for running the example:
5252 * <ol>
5757 * -Dexec.args="[<project_id>] list [<bucket>]| info [<bucket> [<file>]]|
5858 * download <bucket> <path> [local_file]| upload <local_file> <bucket> [<path>]|
5959 * delete <bucket> <path>+| cp <from_bucket> <from_path> <to_bucket> <to_path>|
60- * compose <bucket> <from_path>+ <to_path>"}
60+ * compose <bucket> <from_path>+ <to_path>| update_metadata <bucket> <file> [key=value]* "}
6161 * </li>
6262 * </ol>
63+ *
64+ * The first parameter is an optional project_id (logged-in project will be used if not supplied).
65+ * Second parameter is a Storage operation (list, delete, compose,...) to demonstrate the its
66+ * usage. Any other arguments are specific to the operation.
67+ * See each action's run method for the specific Storage interaction.
6368 */
6469public class StorageExample {
6570
@@ -112,24 +117,36 @@ public String params() {
112117 }
113118 }
114119
120+ /**
121+ * This class demonstrates how to retrieve Bucket or Blob metadata.
122+ * If more than one blob is supplied a Batch operation would be used to get all blobs metadata
123+ * in a single RPC.
124+ *
125+ * @see <a href="https://cloud.google.com/storage/docs/json_api/v1/objects/get">Objects: get</a>
126+ */
115127 private static class InfoAction extends BlobsAction {
116128 @ Override
117129 public void run (StorageService storage , Blob ... blobs ) {
118-
119-
120130 if (blobs .length == 1 ) {
121131 if (blobs [0 ].name ().isEmpty ()) {
122- System .out .println (storage .get (blobs [0 ].bucket ()));
132+ // get Bucket
133+ Bucket bucket = storage .get (blobs [0 ].bucket ());
134+ System .out .println ("Bucket info: " + bucket );
123135 } else {
124- System .out .println (storage .get (blobs [0 ].bucket (), blobs [0 ].name ()));
136+ // get Blob
137+ Blob blob = storage .get (blobs [0 ].bucket (), blobs [0 ].name ());
138+ System .out .println ("Blob info: " + blob );
125139 }
126140 } else {
141+ // use batch to get multiple blobs.
127142 BatchRequest .Builder batch = BatchRequest .builder ();
128143 for (Blob blob : blobs ) {
129144 batch .get (blob .bucket (), blob .name ());
130145 }
131146 BatchResponse response = storage .apply (batch .build ());
132- System .out .println (response .gets ());
147+ for (BatchResponse .Result <Blob > result : response .gets ()) {
148+ System .out .println (result .get ());
149+ }
133150 }
134151 }
135152
@@ -147,22 +164,45 @@ public String params() {
147164 }
148165 }
149166
167+ /**
168+ * This class demonstrates how to delete a blob.
169+ * If more than one blob is supplied a Batch operation would be used to delete all requested
170+ * blobs in a single RPC.
171+ *
172+ * @see <a href="https://cloud.google.com/storage/docs/json_api/v1/objects/delete">Objects: delete</a>
173+ */
150174 private static class DeleteAction extends BlobsAction {
151175 @ Override
152176 public void run (StorageService storage , Blob ... blobs ) {
153177 if (blobs .length == 1 ) {
154- System .out .println (storage .delete (blobs [0 ].bucket (), blobs [0 ].name ()));
178+ boolean wasDeleted = storage .delete (blobs [0 ].bucket (), blobs [0 ].name ());
179+ if (wasDeleted ) {
180+ System .out .println ("Blob " + blobs [0 ] + " was deleted" );
181+ }
155182 } else {
183+ // use batch operation
156184 BatchRequest .Builder batch = BatchRequest .builder ();
157185 for (Blob blob : blobs ) {
158186 batch .delete (blob .bucket (), blob .name ());
159187 }
188+ int index = 0 ;
160189 BatchResponse response = storage .apply (batch .build ());
161- System .out .println (response .deletes ());
190+ for (BatchResponse .Result <Boolean > result : response .deletes ()) {
191+ if (result .get ()) {
192+ // request order is maintained
193+ System .out .println ("Blob " + blobs [index ] + " was deleted" );
194+ }
195+ index ++;
196+ }
162197 }
163198 }
164199 }
165200
201+ /**
202+ * This class demonstrates how to list buckets or a bucket's blobs.
203+ *
204+ * @see <a href="https://cloud.google.com/storage/docs/json_api/v1/objects/list">Objects: list</a>
205+ */
166206 private static class ListAction extends StorageAction <String > {
167207
168208 @ Override
@@ -179,10 +219,12 @@ String parse(String... args) {
179219 @ Override
180220 public void run (StorageService storage , String bucket ) {
181221 if (bucket == null ) {
222+ // list buckets
182223 for (Bucket b : storage .list ()) {
183224 System .out .println (b );
184225 }
185226 } else {
227+ // list a bucket's blobs
186228 for (Blob b : storage .list (bucket )) {
187229 System .out .println (b );
188230 }
@@ -195,13 +237,24 @@ public String params() {
195237 }
196238 }
197239
240+ /**
241+ * This class demonstrates how to create a new Blob or to update its content.
242+ *
243+ * @see <a href="https://cloud.google.com/storage/docs/json_api/v1/objects/insert">Objects: insert</a>
244+ */
198245 private static class UploadAction extends StorageAction <Tuple <Path , Blob >> {
199246 @ Override
200247 public void run (StorageService storage , Tuple <Path , Blob > tuple ) throws Exception {
201- if (Files .size (tuple .x ()) > 1024 ) {
202- try (BlobWriteChannel writer = storage .writer (tuple .y ())) {
248+ run (storage , tuple .x (), tuple .y ());
249+ }
250+
251+ private void run (StorageService storage , Path uploadFrom , Blob blob ) throws IOException {
252+ if (Files .size (uploadFrom ) > 1_000_000 ) {
253+ // When content is not available or large (1MB or more) it is recommended
254+ // to write it in chunks via the blob's channel writer.
255+ try (BlobWriteChannel writer = storage .writer (blob )) {
203256 byte [] buffer = new byte [1024 ];
204- try (InputStream input = Files .newInputStream (tuple . x () )) {
257+ try (InputStream input = Files .newInputStream (uploadFrom )) {
205258 int limit ;
206259 while ((limit = input .read (buffer )) >= 0 ) {
207260 try {
@@ -213,9 +266,11 @@ public void run(StorageService storage, Tuple<Path, Blob> tuple) throws Exceptio
213266 }
214267 }
215268 } else {
216- byte [] bytes = Files .readAllBytes (tuple .x ());
217- System .out .println (storage .create (tuple .y (), bytes ));
269+ byte [] bytes = Files .readAllBytes (uploadFrom );
270+ // create the blob in one request.
271+ storage .create (blob , bytes );
218272 }
273+ System .out .println ("Blob was created" );
219274 }
220275
221276 @ Override
@@ -235,22 +290,37 @@ public String params() {
235290 }
236291 }
237292
293+ /**
294+ * This class demonstrates how read a blob's content.
295+ * The example will dump the content to a local file if one was given or write
296+ * it to stdout otherwise.
297+ *
298+ * @see <a href="https://cloud.google.com/storage/docs/json_api/v1/objects/get">Objects: get</a>
299+ */
238300 private static class DownloadAction extends StorageAction <Tuple <Blob , Path >> {
239301
240302 @ Override
241303 public void run (StorageService storage , Tuple <Blob , Path > tuple ) throws IOException {
242- Blob blob = storage .get (tuple .x ().bucket (), tuple .x ().name ());
304+ run (storage , tuple .x ().bucket (), tuple .x ().name (), tuple .y ());
305+ }
306+
307+ private void run (StorageService storage , String bucket , String blobName , Path downloadTo )
308+ throws IOException {
309+ Blob blob = storage .get (bucket , blobName );
243310 if (blob == null ) {
244311 System .out .println ("No such object" );
245312 return ;
246313 }
247314 PrintStream writeTo = System .out ;
248- if (tuple . y () != null ) {
249- writeTo = new PrintStream (new FileOutputStream (tuple . y () .toFile ()));
315+ if (downloadTo != null ) {
316+ writeTo = new PrintStream (new FileOutputStream (downloadTo .toFile ()));
250317 }
251- if (blob .size () < 1024 ) {
252- writeTo .write (storage .load (blob .bucket (), blob .name ()));
318+ if (blob .size () < 1_000_000 ) {
319+ // Blob is small read all its content in one request
320+ byte [] content = storage .load (blob .bucket (), blob .name ());
321+ writeTo .write (content );
253322 } else {
323+ // When Blob size is big or unknown use the blob's channel reader.
254324 try (BlobReadChannel reader = storage .reader (blob .bucket (), blob .name ())) {
255325 WritableByteChannel channel = Channels .newChannel (writeTo );
256326 ByteBuffer bytes = ByteBuffer .allocate (64 * 1024 );
@@ -261,7 +331,7 @@ public void run(StorageService storage, Tuple<Blob, Path> tuple) throws IOExcept
261331 }
262332 }
263333 }
264- if (tuple . y () == null ) {
334+ if (downloadTo == null ) {
265335 writeTo .println ();
266336 } else {
267337 writeTo .close ();
@@ -291,10 +361,16 @@ public String params() {
291361 }
292362 }
293363
364+ /**
365+ * This class demonstrates how to use the copy command.
366+ *
367+ * @see <a href="https://cloud.google.com/storage/docs/json_api/v1/objects/copy">Objects: copy</a>
368+ */
294369 private static class CopyAction extends StorageAction <CopyRequest > {
295370 @ Override
296371 public void run (StorageService storage , CopyRequest request ) {
297- System .out .println (storage .copy (request ));
372+ Blob copiedBlob = storage .copy (request );
373+ System .out .println ("Copied " + copiedBlob );
298374 }
299375
300376 @ Override
@@ -311,10 +387,16 @@ public String params() {
311387 }
312388 }
313389
390+ /**
391+ * This class demonstrates how to use the compose command.
392+ *
393+ * @see <a href="https://cloud.google.com/storage/docs/json_api/v1/objects/compose">Objects: compose</a>
394+ */
314395 private static class ComposeAction extends StorageAction <ComposeRequest > {
315396 @ Override
316397 public void run (StorageService storage , ComposeRequest request ) {
317- System .out .println (storage .compose (request ));
398+ Blob composedBlob = storage .compose (request );
399+ System .out .println ("Composed " + composedBlob );
318400 }
319401
320402 @ Override
@@ -336,6 +418,54 @@ public String params() {
336418 }
337419 }
338420
421+ /**
422+ * This class demonstrates how to update a blob's metadata.
423+ *
424+ * @see <a href="https://cloud.google.com/storage/docs/json_api/v1/objects/update">Objects: update</a>
425+ */
426+ private static class UpdateMetadata extends StorageAction <Tuple <Blob , Map <String , String >>> {
427+
428+ @ Override
429+ public void run (StorageService storage , Tuple <Blob , Map <String , String >> tuple )
430+ throws IOException {
431+ run (storage , tuple .x ().bucket (), tuple .x ().name (), tuple .y ());
432+ }
433+
434+ private void run (StorageService storage , String bucket , String blobName ,
435+ Map <String , String > metadata ) {
436+ Blob blob = storage .get (bucket , blobName );
437+ if (blob == null ) {
438+ System .out .println ("No such object" );
439+ return ;
440+ }
441+ blob = storage .update (blob .toBuilder ().metadata (metadata ).build ());
442+ System .out .println ("Updated " + blob );
443+ }
444+
445+ @ Override
446+ Tuple <Blob , Map <String , String >> parse (String ... args ) {
447+ if (args .length < 2 ) {
448+ throw new IllegalArgumentException ();
449+ }
450+ Blob blob = Blob .of (args [0 ], args [1 ]);
451+ Map <String , String > metadata = new HashMap <>();
452+ for (int i = 2 ; i < args .length ; i ++) {
453+ int idx = args [i ].indexOf ('=' );
454+ if (idx < 0 ) {
455+ metadata .put (args [i ], "" );
456+ } else {
457+ metadata .put (args [i ].substring (0 , idx ), args [i ].substring (idx + 1 ));
458+ }
459+ }
460+ return Tuple .of (blob , metadata );
461+ }
462+
463+ @ Override
464+ public String params () {
465+ return "<bucket> <path> [local_file]" ;
466+ }
467+ }
468+
339469 static {
340470 ACTIONS .put ("info" , new InfoAction ());
341471 ACTIONS .put ("delete" , new DeleteAction ());
@@ -344,6 +474,7 @@ public String params() {
344474 ACTIONS .put ("download" , new DownloadAction ());
345475 ACTIONS .put ("cp" , new CopyAction ());
346476 ACTIONS .put ("compose" , new ComposeAction ());
477+ ACTIONS .put ("update_metadata" , new UpdateMetadata ());
347478 }
348479
349480 public static void printUsage () {
@@ -378,7 +509,7 @@ public static void main(String... args) throws Exception {
378509 args = Arrays .copyOfRange (args , 1 , args .length );
379510 }
380511 if (action == null ) {
381- System .out .println ("Unrecognized action '" + args [ 1 ] + "' " );
512+ System .out .println ("Unrecognized action. " );
382513 printUsage ();
383514 return ;
384515 }
0 commit comments