Skip to content

Commit 993f6b9

Browse files
evildourjskeet
authored andcommitted
Allow resumable upload session initiation
1 parent 6ecebb6 commit 993f6b9

File tree

4 files changed

+126
-10
lines changed

4 files changed

+126
-10
lines changed

apis/Google.Cloud.Storage.V1/Google.Cloud.Storage.V1.Snippets/StorageClientSnippets.cs

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
using System.IO;
2121
using System.Linq;
2222
using System.Text;
23+
using System.Threading.Tasks;
2324
using Xunit;
2425

2526
namespace Google.Cloud.Storage.V1.Snippets
@@ -272,6 +273,44 @@ public void UploadObject()
272273
Assert.Equal(source, _fixture.WorldLocalFileName);
273274
}
274275

276+
277+
[Fact]
278+
public async Task UploadObjectWithSessionUri()
279+
{
280+
var bucketName = _fixture.BucketName;
281+
282+
// Snippet: UploadObjectWithSessionUri
283+
var client = StorageClient.Create();
284+
var source = "world.txt";
285+
var destination = "places/world.txt";
286+
var contentType = "text/plain";
287+
288+
// var acl = PredefinedAcl.PublicRead // public
289+
var acl = PredefinedObjectAcl.AuthenticatedRead; // private
290+
var options = new UploadObjectOptions { PredefinedAcl = acl };
291+
// Create a temporary uploader so the upload session can be manually initiated without actually uploading.
292+
var tempUploader = client.CreateObjectUploader(bucketName, destination, contentType, new MemoryStream(), options);
293+
var uploadUri = await tempUploader.InitiateSessionAsync();
294+
295+
// Send uploadUri to (unauthenticated) client application, so it can perform the upload:
296+
using (var stream = File.OpenRead(source))
297+
{
298+
// IUploadProgress defined in Google.Apis.Upload namespace
299+
IProgress<IUploadProgress> progress = new Progress<IUploadProgress>(
300+
p => Console.WriteLine($"bytes: {p.BytesSent}, status: {p.Status}")
301+
);
302+
303+
var actualUploader = ResumableUpload.CreateFromUploadUri(uploadUri, stream);
304+
actualUploader.ProgressChanged += progress.Report;
305+
await actualUploader.UploadAsync();
306+
}
307+
// End snippet
308+
309+
// want to show the source in the snippet, but also
310+
// want to make sure it matches the one in the fixture
311+
Assert.Equal(source, _fixture.WorldLocalFileName);
312+
}
313+
275314
[Fact]
276315
public void GetObject()
277316
{

apis/Google.Cloud.Storage.V1/Google.Cloud.Storage.V1/StorageClient.UploadObject.cs

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
// See the License for the specific language governing permissions and
1313
// limitations under the License.
1414

15+
using Google.Apis.Storage.v1;
1516
using Google.Apis.Upload;
1617
using System;
1718
using System.IO;
@@ -23,6 +24,46 @@ namespace Google.Cloud.Storage.V1
2324
{
2425
public abstract partial class StorageClient
2526
{
27+
/// <summary>
28+
/// Creates an instance which is capable of starting a resumable upload for an object.
29+
/// </summary>
30+
/// <param name="bucket">The name of the bucket containing the object. Must not be null.</param>
31+
/// <param name="objectName">The name of the object within the bucket. Must not be null.</param>
32+
/// <param name="contentType">The content type of the object. This should be a MIME type
33+
/// such as "text/html" or "application/octet-stream". May be null.</param>
34+
/// <param name="source">The stream to read the data from. Must not be null.</param>
35+
/// <param name="options">Additional options for the upload. May be null, in which case appropriate
36+
/// defaults will be used.</param>
37+
/// <returns>The <see cref="ObjectsResource.InsertMediaUpload"/> which can be used to upload the object.</returns>
38+
/// <seealso cref="UploadObject(Object,Stream,UploadObjectOptions,IProgress{IUploadProgress})"/>
39+
public virtual ObjectsResource.InsertMediaUpload CreateObjectUploader(
40+
string bucket,
41+
string objectName,
42+
string contentType,
43+
Stream source,
44+
UploadObjectOptions options = null)
45+
{
46+
throw new NotImplementedException();
47+
}
48+
49+
/// <summary>
50+
/// Creates an instance which is capable of starting a resumable upload for an object.
51+
/// </summary>
52+
/// <param name="destination">Object to create or update. Must not be null, and must have the name,
53+
/// bucket and content type populated.</param>
54+
/// <param name="source">The stream to read the data from. Must not be null.</param>
55+
/// <param name="options">Additional options for the upload. May be null, in which case appropriate
56+
/// defaults will be used.</param>
57+
/// <returns>The <see cref="ObjectsResource.InsertMediaUpload"/> which can be used to upload the object.</returns>
58+
/// <seealso cref="UploadObject(Object,Stream,UploadObjectOptions,IProgress{IUploadProgress})"/>
59+
public virtual ObjectsResource.InsertMediaUpload CreateObjectUploader(
60+
Object destination,
61+
Stream source,
62+
UploadObjectOptions options = null)
63+
{
64+
throw new NotImplementedException();
65+
}
66+
2667
/// <summary>
2768
/// Uploads the data for an object in storage synchronously, from a specified stream.
2869
/// </summary>

apis/Google.Cloud.Storage.V1/Google.Cloud.Storage.V1/StorageClientImpl.UploadObject.cs

Lines changed: 32 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
// limitations under the License.
1414

1515
using Google.Api.Gax;
16+
using Google.Apis.Storage.v1;
1617
using Google.Apis.Upload;
1718
using System;
1819
using System.IO;
@@ -24,6 +25,35 @@ namespace Google.Cloud.Storage.V1
2425
{
2526
public sealed partial class StorageClientImpl : StorageClient
2627
{
28+
/// <inheritdoc />
29+
public override ObjectsResource.InsertMediaUpload CreateObjectUploader(
30+
string bucket,
31+
string objectName,
32+
string contentType,
33+
Stream source,
34+
UploadObjectOptions options = null)
35+
{
36+
ValidateBucketName(bucket);
37+
GaxPreconditions.CheckNotNull(objectName, nameof(objectName));
38+
return CreateObjectUploader(
39+
new Object { Bucket = bucket, Name = objectName, ContentType = contentType },
40+
source, options);
41+
}
42+
43+
/// <inheritdoc />
44+
public override ObjectsResource.InsertMediaUpload CreateObjectUploader(
45+
Object destination,
46+
Stream source,
47+
UploadObjectOptions options = null)
48+
{
49+
ValidateObject(destination, nameof(destination));
50+
GaxPreconditions.CheckNotNull(source, nameof(source));
51+
var mediaUpload = new CustomMediaUpload(Service, destination, destination.Bucket, source, destination.ContentType);
52+
options?.ModifyMediaUpload(mediaUpload);
53+
ApplyEncryptionKey(options?.EncryptionKey, mediaUpload);
54+
return mediaUpload;
55+
}
56+
2757
/// <inheritdoc />
2858
public override Object UploadObject(
2959
string bucket,
@@ -63,11 +93,7 @@ public override Object UploadObject(
6393
UploadObjectOptions options = null,
6494
IProgress<IUploadProgress> progress = null)
6595
{
66-
ValidateObject(destination, nameof(destination));
67-
GaxPreconditions.CheckNotNull(source, nameof(source));
68-
var mediaUpload = new CustomMediaUpload(Service, destination, destination.Bucket, source, destination.ContentType);
69-
options?.ModifyMediaUpload(mediaUpload);
70-
ApplyEncryptionKey(options?.EncryptionKey, mediaUpload);
96+
var mediaUpload = CreateObjectUploader(destination, source, options);
7197
if (progress != null)
7298
{
7399
mediaUpload.ProgressChanged += progress.Report;
@@ -89,11 +115,7 @@ public override async Task<Object> UploadObjectAsync(
89115
CancellationToken cancellationToken = default(CancellationToken),
90116
IProgress<IUploadProgress> progress = null)
91117
{
92-
ValidateObject(destination, nameof(destination));
93-
GaxPreconditions.CheckNotNull(source, nameof(source));
94-
var mediaUpload = new CustomMediaUpload(Service, destination, destination.Bucket, source, destination.ContentType);
95-
options?.ModifyMediaUpload(mediaUpload);
96-
ApplyEncryptionKey(options?.EncryptionKey, mediaUpload);
118+
var mediaUpload = CreateObjectUploader(destination, source, options);
97119
if (progress != null)
98120
{
99121
mediaUpload.ProgressChanged += progress.Report;

apis/Google.Cloud.Storage.V1/docs/index.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,20 @@ Or write-only access to put specific object content into a bucket:
4848

4949
[!code-cs[](obj/snippets/Google.Cloud.Storage.V1.UrlSigner.txt#SignedURLPut)]
5050

51+
## Upload URIs
52+
53+
In some cases, it may not make sense for client applications to have permissions
54+
to begin an upload for an object, but an authenticated service may choose to grant
55+
this ability for individual uploads. Signed URLs are one option for this. Another
56+
option is for the service to start a resumable upload session, but instead of
57+
performing the upload, sending the resulting upload URI to the client application
58+
so it can perform the upload instead. Unlike sessions initiated with a signed URL,
59+
a pre-initated upload session will force the client application to upload through
60+
the region in which the session began, which will likely be close to the service,
61+
and not necessarily the client.
62+
63+
[!code-cs[](obj/snippets/Google.Cloud.Storage.V1.StorageClient.txt#UploadObjectWithSessionUri)]
64+
5165
## Customer-supplied encryption keys
5266

5367
Storage objects are always stored encrypted, but if you wish to

0 commit comments

Comments
 (0)