示例#1
0
 /**
  * @param string $path
  *
  * @param WriteMode $writeMode
  *    What to do if there's already a file at the given path (UTF-8).
  *
  * @param resource $inStream
  *    The source of data to upload.
  *
  * @param int|null $numBytes
  *    You can pass in <code>null</code>.  But if you know how many bytes you expect, pass in
  *    that value and this function will do a sanity check at the end to make sure the number of
  *    bytes read from $inStream matches up.
  *
  * @param int $chunkSize
  *
  * @return array
  *    The <a href="https://www.dropbox.com/developers/core/docs#metadata-details>metadata
  *    object</a> for the newly-added file.
  */
 private function _uploadFileChunked($path, $writeMode, $inStream, $numBytes, $chunkSize)
 {
     Path::checkArg("path", $path);
     WriteMode::checkArg("writeMode", $writeMode);
     Checker::argResource("inStream", $inStream);
     Checker::argNatOrNull("numBytes", $numBytes);
     Checker::argNat("chunkSize", $chunkSize);
     // NOTE: This function performs 3 retries on every call.  This is maybe not the right
     // layer to make retry decisions.  It's also awkward because none of the other calls
     // perform retries.
     assert($chunkSize > 0);
     $data = self::readFully($inStream, $chunkSize);
     $len = strlen($data);
     $client = $this;
     $uploadId = RequestUtil::runWithRetry(3, function () use($data, $client) {
         return $client->chunkedUploadStart($data);
     });
     $byteOffset = $len;
     while (!feof($inStream)) {
         $data = self::readFully($inStream, $chunkSize);
         $len = strlen($data);
         while (true) {
             $r = RequestUtil::runWithRetry(3, function () use($client, $uploadId, $byteOffset, $data) {
                 return $client->chunkedUploadContinue($uploadId, $byteOffset, $data);
             });
             if ($r === true) {
                 // Chunk got uploaded!
                 $byteOffset += $len;
                 break;
             }
             if ($r === false) {
                 // Server didn't recognize our upload ID
                 // This is very unlikely since we're uploading all the chunks in sequence.
                 throw new Exception_BadResponse("Server forgot our uploadId");
             }
             // Otherwise, the server is at a different byte offset from us.
             $serverByteOffset = $r;
             assert($serverByteOffset !== $byteOffset);
             // chunkedUploadContinue ensures this.
             // An earlier byte offset means the server has lost data we sent earlier.
             if ($serverByteOffset < $byteOffset) {
                 throw new Exception_BadResponse("Server is at an ealier byte offset: us={$byteOffset}, server={$serverByteOffset}");
             }
             $diff = $serverByteOffset - $byteOffset;
             // If the server is past where we think it could possibly be, something went wrong.
             if ($diff > $len) {
                 throw new Exception_BadResponse("Server is more than a chunk ahead: us={$byteOffset}, server={$serverByteOffset}");
             }
             // The normal case is that the server is a bit further along than us because of a
             // partially-uploaded chunk.  Finish it off.
             $byteOffset += $diff;
             if ($diff === $len) {
                 break;
             }
             // If the server is at the end, we're done.
             $data = substr($data, $diff);
         }
     }
     if ($numBytes !== null && $byteOffset !== $numBytes) {
         throw new \InvalidArgumentException("You passed numBytes={$numBytes} but the stream had {$byteOffset} bytes.");
     }
     $metadata = RequestUtil::runWithRetry(3, function () use($client, $uploadId, $path, $writeMode) {
         return $client->chunkedUploadFinish($uploadId, $path, $writeMode);
     });
     return $metadata;
 }