1 // Copyright 2020 Google LLC
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #include "google/cloud/storage/client.h"
16 #include "google/cloud/storage/examples/storage_examples_common.h"
17 #include "google/cloud/storage/oauth2/google_credentials.h"
18 #include "google/cloud/storage/parallel_upload.h"
19 #include "google/cloud/storage/well_known_parameters.h"
20 #include "google/cloud/internal/getenv.h"
21 #include <fstream>
22 #include <iostream>
23 #include <map>
24 #include <sstream>
25 #include <string>
26 #include <thread>
27 
28 namespace {
StartResumableUpload(google::cloud::storage::Client client,std::vector<std::string> const & argv)29 void StartResumableUpload(google::cloud::storage::Client client,
30                           std::vector<std::string> const& argv) {
31   //! [start resumable upload]
32   namespace gcs = google::cloud::storage;
33   [](gcs::Client client, std::string const& bucket_name,
34      std::string const& object_name) {
35     gcs::ObjectWriteStream stream = client.WriteObject(
36         bucket_name, object_name, gcs::NewResumableUploadSession());
37     std::cout << "Created resumable upload: " << stream.resumable_session_id()
38               << "\n";
39     // As it is customary in C++, the destructor automatically closes the
40     // stream, that would finish the upload and create the object. For this
41     // example we want to restore the session as-if the application had crashed,
42     // where no destructors get called.
43     stream << "This data will not get uploaded, it is too small\n";
44     std::move(stream).Suspend();
45   }
46   //! [start resumable upload]
47   (std::move(client), argv.at(0), argv.at(1));
48 }
49 
ResumeResumableUpload(google::cloud::storage::Client client,std::vector<std::string> const & argv)50 void ResumeResumableUpload(google::cloud::storage::Client client,
51                            std::vector<std::string> const& argv) {
52   //! [resume resumable upload]
53   namespace gcs = google::cloud::storage;
54   using ::google::cloud::StatusOr;
55   [](gcs::Client client, std::string const& bucket_name,
56      std::string const& object_name, std::string const& session_id) {
57     // Restore a resumable upload stream, the library automatically queries the
58     // state of the upload and discovers the next expected byte.
59     gcs::ObjectWriteStream stream =
60         client.WriteObject(bucket_name, object_name,
61                            gcs::RestoreResumableUploadSession(session_id));
62     if (!stream.IsOpen() && stream.metadata().ok()) {
63       std::cout << "The upload has already been finalized.  The object "
64                 << "metadata is: " << *stream.metadata() << "\n";
65     }
66     if (stream.next_expected_byte() == 0) {
67       // In this example we create a small object, smaller than the resumable
68       // upload quantum (256 KiB), so either all the data is there or not.
69       // Applications use `next_expected_byte()` to find the position in their
70       // input where they need to start uploading.
71       stream << R"""(
72 Lorem ipsum dolor sit amet, consectetur adipiscing
73 elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim
74 ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea
75 commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit
76 esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat
77 non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
78 )""";
79     }
80 
81     stream.Close();
82 
83     StatusOr<gcs::ObjectMetadata> metadata = stream.metadata();
84     if (!metadata) throw std::runtime_error(metadata.status().message());
85     std::cout << "Upload completed, the new object metadata is: " << *metadata
86               << "\n";
87   }
88   //! [resume resumable upload]
89   (std::move(client), argv.at(0), argv.at(1), argv.at(2));
90 }
91 
RunAll(std::vector<std::string> const & argv)92 void RunAll(std::vector<std::string> const& argv) {
93   namespace examples = ::google::cloud::storage::examples;
94   namespace gcs = ::google::cloud::storage;
95 
96   if (!argv.empty()) throw examples::Usage{"auto"};
97   examples::CheckEnvironmentVariablesAreSet({
98       "GOOGLE_CLOUD_PROJECT",
99       "GOOGLE_CLOUD_CPP_STORAGE_TEST_BUCKET_NAME",
100   });
101   auto const project_id =
102       google::cloud::internal::GetEnv("GOOGLE_CLOUD_PROJECT").value();
103   auto const bucket_name = google::cloud::internal::GetEnv(
104                                "GOOGLE_CLOUD_CPP_STORAGE_TEST_BUCKET_NAME")
105                                .value();
106   auto generator = google::cloud::internal::DefaultPRNG(std::random_device{}());
107   auto const object_name =
108       examples::MakeRandomObjectName(generator, "ob-resumable-upload-");
109 
110   auto client = gcs::Client::CreateDefaultClient().value();
111 
112   std::cout << "\nRunning StartResumableUpload() example" << std::endl;
113   StartResumableUpload(client, {bucket_name, object_name});
114 
115   std::cout << "\nCreating and capturing new resumable session id" << std::endl;
116   auto session_id = [&] {
117     auto stream = client.WriteObject(bucket_name, object_name,
118                                      gcs::NewResumableUploadSession());
119     auto id = stream.resumable_session_id();
120     std::move(stream).Suspend();
121     return id;
122   }();
123 
124   std::cout << "\nRunning ResumeResumableUpload() example" << std::endl;
125   ResumeResumableUpload(client, {bucket_name, object_name, session_id});
126 
127   (void)client.DeleteObject(bucket_name, object_name);
128 }
129 
130 }  // namespace
131 
main(int argc,char * argv[])132 int main(int argc, char* argv[]) {
133   namespace examples = ::google::cloud::storage::examples;
134   auto make_entry = [](std::string const& name,
135                        std::vector<std::string> arg_names,
136                        examples::ClientCommand const& cmd) {
137     arg_names.insert(arg_names.begin(), {"<bucket-name>", "<object-name>"});
138     return examples::CreateCommandEntry(name, std::move(arg_names), cmd);
139   };
140 
141   examples::Example example({
142       make_entry("start-resumable-upload", {}, StartResumableUpload),
143       make_entry("resume-resumable-upload", {"<session-id>"},
144                  ResumeResumableUpload),
145       {"auto", RunAll},
146   });
147   return example.Run(argc, argv);
148 }
149