@@ -1044,11 +1044,13 @@ func (c *grpcStorageClient) OpenWriter(params *openWriterParams, opts ...storage
10441044 }
10451045
10461046 // The chunk buffer is full, but there is no end in sight. This
1047- // means that a resumable upload will need to be used to send
1047+ // means that either:
1048+ // 1. A resumable upload will need to be used to send
10481049 // multiple chunks, until we are done reading data. Start a
10491050 // resumable upload if it has not already been started.
1050- // Otherwise, all data will be sent over a single gRPC stream.
1051- if ! doneReading && gw .upid == "" {
1051+ // 2. ChunkSize of zero may also have a full buffer, but a resumable
1052+ // session should not be initiated in this case.
1053+ if ! doneReading && gw .upid == "" && params .chunkSize != 0 {
10521054 err = gw .startResumableUpload ()
10531055 if err != nil {
10541056 err = checkCanceled (err )
@@ -1065,11 +1067,15 @@ func (c *grpcStorageClient) OpenWriter(params *openWriterParams, opts ...storage
10651067 pr .CloseWithError (err )
10661068 return
10671069 }
1070+
10681071 // At this point, the current buffer has been uploaded. For resumable
1069- // uploads, capture the committed offset here in case the upload was not
1070- // finalized and another chunk is to be uploaded.
1071- if gw .upid != "" {
1072+ // uploads and chunkSize = 0, capture the committed offset here in case
1073+ // the upload was not finalized and another chunk is to be uploaded. Call
1074+ // the progress function for resumable uploads only.
1075+ if gw .upid != "" || gw .chunkSize == 0 {
10721076 offset = off
1077+ }
1078+ if gw .upid != "" {
10731079 progress (offset )
10741080 }
10751081
@@ -1485,14 +1491,11 @@ func newGRPCWriter(c *grpcStorageClient, params *openWriterParams, r io.Reader)
14851491 size += googleapi .MinUploadChunkSize - (size % googleapi .MinUploadChunkSize )
14861492 }
14871493
1494+ // A completely bufferless upload is not possible as it is in JSON because
1495+ // the buffer must be provided to the message. However use the minimum size
1496+ // possible in this case.
14881497 if params .chunkSize == 0 {
1489- // TODO: Should we actually use the minimum of 256 KB here when the user
1490- // indicates they want minimal memory usage? We cannot do a zero-copy,
1491- // bufferless upload like HTTP/JSON can.
1492- // TODO: We need to determine if we can avoid starting a
1493- // resumable upload when the user *plans* to send more than bufSize but
1494- // with a bufferless upload.
1495- size = maxPerMessageWriteSize
1498+ size = googleapi .MinUploadChunkSize
14961499 }
14971500
14981501 return & gRPCWriter {
@@ -1505,6 +1508,7 @@ func newGRPCWriter(c *grpcStorageClient, params *openWriterParams, r io.Reader)
15051508 conds : params .conds ,
15061509 encryptionKey : params .encryptionKey ,
15071510 sendCRC32C : params .sendCRC32C ,
1511+ chunkSize : params .chunkSize ,
15081512 }
15091513}
15101514
@@ -1524,6 +1528,7 @@ type gRPCWriter struct {
15241528 settings * settings
15251529
15261530 sendCRC32C bool
1531+ chunkSize int
15271532
15281533 // The gRPC client-stream used for sending buffers.
15291534 stream storagepb.Storage_WriteObjectClient
@@ -1589,7 +1594,6 @@ func (w *gRPCWriter) uploadBuffer(recvd int, start int64, doneReading bool) (*st
15891594 offset := start
15901595 toWrite := w .buf [:recvd ]
15911596 for {
1592- first := sent == 0
15931597 // This indicates that this is the last message and the remaining
15941598 // data fits in one message.
15951599 belowLimit := recvd - sent <= limit
@@ -1612,10 +1616,10 @@ func (w *gRPCWriter) uploadBuffer(recvd int, start int64, doneReading bool) (*st
16121616 FinishWrite : finishWrite ,
16131617 }
16141618
1615- // Open a new stream and set the first_message field on the request.
1616- // The first message on the WriteObject stream must either be the
1617- // Object or the Resumable Upload ID.
1618- if first {
1619+ // Open a new stream if necessary and set the first_message field on
1620+ // the request. The first message on the WriteObject stream must either
1621+ // be the Object or the Resumable Upload ID.
1622+ if w . stream == nil {
16191623 ctx := gapic .InsertMetadata (w .ctx , metadata .Pairs ("x-goog-request-params" , fmt .Sprintf ("bucket=projects/_/buckets/%s" , url .QueryEscape (w .bucket ))))
16201624 w .stream , err = w .c .raw .WriteObject (ctx )
16211625 if err != nil {
@@ -1678,6 +1682,13 @@ func (w *gRPCWriter) uploadBuffer(recvd int, start int64, doneReading bool) (*st
16781682 continue
16791683 }
16801684
1685+ // The buffer has been uploaded and there is still more data to be
1686+ // uploaded, but this is not a resumable upload session. Therefore
1687+ // keep the stream open and don't commit yet.
1688+ if ! finishWrite && w .chunkSize == 0 {
1689+ return nil , offset , false , nil
1690+ }
1691+
16811692 // Done sending data. Close the stream to "commit" the data sent.
16821693 resp , finalized , err := w .commit ()
16831694 // Retriable errors mean we should start over and attempt to
0 commit comments