Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Make requests and handle responses for resumable upload: Google Drive api [php]

I'm using the API Client Library for PHP (Beta) to work with google drive api, So far I can authorize and and upload a file in chuncks. According to the documentation, these three steps should be taken to upload a file:

  • Start a resumable session.
  • Save the resumable session URI.
  • Upload the file.

Which I think the Client Library handles. Again, according to the documentation, if I want to show the progress or resume an interrupted upload, or to handle errors I need to capture the response and also be able to send requests like this:

> PUT {session_uri} HTTP/1.1 Content-Length: 0 Content-Range: bytes
> */2000000

But I have no idea how should I make such request and where can I get the response from, the php code I'm using to upload,like any other php code, only returns values when it is done executing, which is when the upload is done. here is the function I'm using to upload files (Resumable):

function uploadFile($service,$client,$filetoUpload,$parentId){
    $file = new Google_Service_Drive_DriveFile();
    $file->title = $filetoUpload['name'];
    $chunkSizeBytes = 1 * 1024 * 1024;
    
    // Set the parent folder.
      if ($parentId != null) {
        $parent = new Google_Service_Drive_ParentReference();
        $parent->setId($parentId);
        $file->setParents(array($parent));
      }
  
    // Call the API with the media upload, defer so it doesn't immediately return.
    $client->setDefer(true);
    $request = $service->files->insert($file);
    
    // Create a media file upload to represent our upload process.
    $media = new Google_Http_MediaFileUpload(
      $client,
      $request,
      $filetoUpload['type'],
      null,
      true,
      $chunkSizeBytes
    );
    $media->setFileSize(filesize($filetoUpload['tmp_name']));
    
    // Upload the various chunks. $status will be false until the process is
    // complete.
    $status = false;
    $handle = fopen($filetoUpload['tmp_name'], "rb");

    while (!$status && !feof($handle)) {
      set_time_limit(120);    
      $chunk = fread($handle, $chunkSizeBytes);
      $status = $media->nextChunk($chunk);
     }
    
    // The final value of $status will be the data from the API for the object
    // that has been uploaded.
    $result = false;
    if($status != false) {
      $result = $status;
      set_time_limit(30);
      echo "<pre>";
      print_r($result);
    }
    
    fclose($handle);
    // Reset to the client to execute requests immediately in the future.
    $client->setDefer(false);
}

Should i make a separate php file to handle these requests? if so, How should tell that which file's status I'm requesting for? Thanks.

like image 913
Ramtin Gh Avatar asked Sep 03 '15 14:09

Ramtin Gh


1 Answers

Apperantly, the BETA Client API simply doesn't support resuming Uploads. Please see this issue on Github, which asks for fixing this. Of course it should be easy to modify the class (see below) and create a Pull-request to enable support for resuming existing uploads when the session-URL is supplied.

However, there's an easy way to get the progress after an chunk has been uploaded. The Google_Http_MediaFileUpload-object ($media in your example) has a public method called 'getProgress' which can be called anytime. (Please have a look at the source code of the API-client-library).

To get the upload status, I'd add a parameter to modify the progress precision by adjusting the chunk size. Since the more chunks are used, the more protocol overhead is generated, setting the precision as low as possible should be avoided.

Therefore, you could modify your source code as below to output the progress after each chunk:

function uploadFile($service,$client,$filetoUpload,$parentId,$progressPrecision = 1){
$file = new Google_Service_Drive_DriveFile();
$file->title = $filetoUpload['name'];
$filesize = filesize($filetoUpload['tmp_name']);
//minimum chunk size needs to be 256K
$chunkSizeBytes = min( $filesize / 100 * $progressPrecision, 262144);

// Set the parent folder.
  if ($parentId != null) {
    $parent = new Google_Service_Drive_ParentReference();
    $parent->setId($parentId);
    $file->setParents(array($parent));
  }

// Call the API with the media upload, defer so it doesn't immediately return.
$client->setDefer(true);
$request = $service->files->insert($file);

// Create a media file upload to represent our upload process.
$media = new Google_Http_MediaFileUpload(
  $client,
  $request,
  $filetoUpload['type'],
  null,
  true,
  $chunkSizeBytes
);
$media->setFileSize($filesize);

while (!$status && !feof($handle)) {
  set_time_limit(120);    
  $chunk = fread($handle, $chunkSizeBytes);
  $status = $media->nextChunk($chunk);
  if(!$status){ //nextChunk() returns 'false' whenever the upload is still in progress
   echo 'sucessfully uploaded file up to byte ' . $media->getProgress() . 
   ' which is ' . ( $media->getProgress() / $chunkSizeBytes ) . '% of the whole file';
  }
 }

Hope this helps. I'll see if I can find some time to add resume-support to the client Library.

EDIT: according to this doc, the chunks need to be at least 256KB big. Changed in code.

EDIT2: I just added a Pull request To add the Resume-Feature. If it gets rejected you could still decide whether it would be ok for you to modify/extend the client. If it gets accepted, just use store the return value of $media->getResumeUri() in a database, and later call $media->resume($previously_stored_return_value) after instantiation to resume the process.

like image 76
tillz Avatar answered Sep 30 '22 03:09

tillz