Skip to content

Instantly share code, notes, and snippets.

@ivanvermeyen
Last active March 15, 2023 05:25
Show Gist options
  • Star 72 You must be signed in to star a gist
  • Fork 29 You must be signed in to fork a gist
  • Save ivanvermeyen/cc7c59c185daad9d4e7cb8c661d7b89b to your computer and use it in GitHub Desktop.
Save ivanvermeyen/cc7c59c185daad9d4e7cb8c661d7b89b to your computer and use it in GitHub Desktop.
Setup a Laravel Storage driver with Google Drive API

Get started with Google Drive and Laravel

This gist has moved!

This gist was getting a lot of comments/questions, but since there are no notifications when someone replies to a gist, I've moved the setup instructions and a bunch of sample code to a dedicated Github repo.

API key walkthrough

Step by step instructions on how to get the various keys needed to work with Google Drive can be found on the Github repo as well:

Find examples

Take a look at the commit history to get a quick overview of the demo code I've been adding:

Got questions?

If you have questions or comments, please create an issue on the Github repo. This is easier to follow up and enables others to find the answers as well.

@DannyAndres
Copy link

DannyAndres commented Jul 24, 2017

hey, great work! but i have an issue to save files on folders, how can i do that?.... i can't simply pass the folder's name, for example
Storage::cloud()->put('folderName/fileName.extension', File::get($file));
this does not work....

pd: i think this solves the problem
IDfolder, is the folder that i want to save things
Storage::cloud()->put('IDfolder/folderName/fileName.extension', File::get($file));

@ivanvermeyen
Copy link
Author

ivanvermeyen commented Jul 24, 2017

Hey, I think the main problem with Google Drive is that file and folder names are not unique.
So you must use the ID (path) instead of the name...
This makes it a bit complicated to work with the filesystem, since you need to get the ID every time.

Check this demo code I've just added:
https://github.com/ivanvermeyen/laravel-google-drive-demo/blob/master/routes/web.php#L54-L75

@agbeterseer
Copy link

agbeterseer commented Jul 25, 2017

hey ivanvermeyen, doing a great job man. i have and issue here, am able to upload files to google drive but i also want to save the path to my Mysql DB. how can i achieve this.

this uploads the file to google drive but the path saved to my DB is from my local serve.
so how can get the path of the stored file on Google Drive.
//get the file name getClientOriginalName
$fileName = $file->getClientOriginalName();
$destinationPath = config('fileDestinationPath').'/'.$fileName;

     $uploaded = Storage::cloud()->put($destinationPath, file_get_contents($file->getRealPath()));

    if ($uploaded) {
    //create new object
    $indocument = new Document;

    //get input and store into variables set all input to insert to db
    $indocument->candidates_name = $request->candidates_name;
    $indocument->years_of_experience = $request->years_of_experience;
    $indocument->city_id = $request->location;
    $indocument->save();


    if ($indocument) {
       
    $indocument = Document::find($indocument->id);
    $indocument->cv_file=$destinationPath;
    $indocument->save();
    
    }

@ivanvermeyen
Copy link
Author

I don't think there is any easy way to get a reference to a file when it is uploaded.

The way I would do it is upload the file, then get a fresh file listing, order by the timestamp and get the latest file that has that name...
If you have lots of simultaneous uploads and someone else uploads a file with the same name at the same time, you could get a reference to the wrong file... but I think those chances are minimal?

This would look something like this:
https://github.com/ivanvermeyen/laravel-google-drive-demo/blob/master/routes/web.php#L77-L93

@agbeterseer
Copy link

I modified your code and i was able to get the reference file i uploaded Google drive to my database

thanks very much.

$fileName = $file->getClientOriginalName();
$dir = '/';
$recursive = false; // Get subdirectories also?
$contents = collect(Storage::cloud()->listContents($dir, $recursive));
$dir = $contents->where('type', '=', 'dir')->first();
if ( ! $dir) {
return 'Directory does not exist!';
}
$pathName = $dir['path'].$fileName;
$uploaded = Storage::cloud()->put($pathName, file_get_contents($file->getRealPath()));
if ($uploaded) {
//create new object
$indocument = new Document;
//get input and store into variables set all input to insert to db
$indocument->candidates_name = $request->candidates_name;
$indocument->years_of_experience = $request->years_of_experience;
$indocument->city_id = $request->location;
$indocument->save();

    // dd($destinationPath);
    if ($indocument) {
    $indocument = Document::find($indocument->id);
    $indocument->cv_file=$pathName;
    $indocument->save();
    
    }

@ivanvermeyen
Copy link
Author

This is good to store the file:

$pathName = $dir['path'].$fileName;

But don't you need the path of the uploaded file to save it in the database?
Now $fileName is still the original file name?

I'm thinking about writing some kind of wrapper class to work with the Google Drive file system with its ID's and duplicate names...

@agbeterseer
Copy link

Yes, that would nice
and i do need the path to save to database.
for now i just have the fileName and directory/fileName stored in the db.

i am uploading files of type doc, docx, pdf, and txt.

Now is there a way to render a word file open on the browser from laravel application when i view all ?

remember these files will be loading from Google Drive

@ivanvermeyen
Copy link
Author

There seem to be packages that enable you to create/edit Word files, but I'm not sure if you can show the contents on a webpage... Wouldn't you need to convert it to HTML or something? Seems a bit weird to me.

You can let people download the Word file though:

$rawData = Storage::cloud()->get($filePath);

return response($rawData, 200)
    ->header('ContentType','application/vnd.openxmlformats-officedocument.wordprocessingml.document')
    ->header('Content-Disposition', 'attachment; filename="download.docx');

@guilhermeof
Copy link

Good afternoon,
How do I get the url to download the file?

@ivanvermeyen
Copy link
Author

@guilhermeof I don't know if you can get a direct link to a file... The file would also need to be publicly shared etc...

But you can generate a link on your own website and let people download the file from your site directly.

When you list the contents of Google Drive, it returns an array.
Each array item is also an array, containing file or folder info, including a path.
If you get this path, you will get the raw file data, which you can use to force a download (see my previous reply).

To see some sample code on how to get the raw data, check this out:
https://github.com/ivanvermeyen/laravel-google-drive-demo/blob/master/routes/web.php#L77-L93

@Brabas
Copy link

Brabas commented Aug 8, 2017

Hi, Thank you for your great work.

Want to download a folder with its contents, I modified the following function in web.php

Route::get('get', function() {

  $filename = 'appply';        //name of folder

  $dir = '/';
  $recursive = false; // Get subdirectories also?
  $contents = collect(Storage::cloud()->listContents($dir, $recursive));

  $file = $contents
      ->where('type', '=', 'dir')
      ->where('filename', '=', pathinfo($filename, PATHINFO_FILENAME))
      ->where('extension', '=', pathinfo($filename, PATHINFO_EXTENSION))
      ->first(); // there can be duplicate file names!

  $rawData = `Storage::cloud()->get($file['path']);

  return response($rawData, 200)
      ->header('ContentType', $file['mimetype'])
      ->header('Content-Disposition', "attachment; filename='$filename'");
});

But I'm throwing the following error

(1/1) Google_Service_Exception
{
"error": {
"errors": [
{
"domain": "global",
"reason": "fileNotDownloadable",
"message": "Only files with binary content can be downloaded. Use Export with Google Docs files.",
"locationType": "parameter",
"location": "alt"
}
],
"code": 403,
"message": "Only files with binary content can be downloaded. Use Export with Google Docs files."
}
}

I try to call the method according to the documentation of the api,

rawData = Storage::cloud()->export($file['path'], 'application/vnd.google-apps.folder');

But it seems to me that I do not do it in the right place.

(1/1) BadMethodCallException
 Call to undefined method League\Flysystem\Filesystem::export

Could you guide me in what class should I modify to make it work?

@ivanvermeyen
Copy link
Author

ivanvermeyen commented Aug 8, 2017

@Brabas if I understand it correctly, you are trying to download all files in the appply folder?

I don't think you can download a whole folder at once in a browser, but instead you can download each file individually.
So first get all files inside the folder, and then loop over the contents (files) and download them one by one...

If you want the end user to only need to download a single item, maybe you can save the files on the server and create a zip file or something? There's probably a package for that... Or you can just generate a download link for each file, because I don't think you can download multiple files in a single request...

Take a look at this sample code I just wrote...

Test Dir would then become appply.
I did assume that the appply folder is in the root of your Google Drive, just to keep it simple.

Hope this helps...

@Brabas
Copy link

Brabas commented Aug 10, 2017

Thank you very much for the guidance

@Arman8852
Copy link

I am getting error while trying to upload file using this controller method:

$files = $request->file('files');

if($request->hasFile('files')){
foreach ($files as $file) {
$disk = Storage::disk('google'); //for local, Storage::disk('local);
$disk->put($name, fopen($file, 'r+'));

      }
             }

When I am trying to uplaod in local storage.It's working fine.
But in case of Google storage,I am getting this error:

(1/1) ErrorException
A non well formed numeric value encountered

in GoogleDriveAdapter.php (line 1143)
at HandleExceptions->handleError(8, 'A non well formed numeric value encountered', '/app/vendor/nao-pon/flysystem-google-drive/src/GoogleDriveAdapter.php', 1143, array('iniName' => 'memory_limit', 'val' => '128M', 'last' => 'm'))in GoogleDriveAdapter.php (line 1143)](url)

@mulqan
Copy link

mulqan commented Oct 18, 2017

hey, how can i upload image with this?

@nimshavb
Copy link

I need service account integration with google drive access using laravel. Refresh token get expired within the time limit. Plz help

@Dimona
Copy link

Dimona commented Nov 10, 2017

Done all by your steps. Added all constants into .env and have the following problem on /list route

@szabizs
Copy link

szabizs commented Feb 7, 2018

@Dimona Did you manage to solve this problem?
I'm facing the exact same problem.

@ivanvermeyen
Copy link
Author

Only on the /list route? Seems to be related to authentication... If no routes are working, double check the keys in .env and the filesystems.php config file.

If the problem persist, please create an issue on the GitHub repo. It's hard to keep track of Gist comments (no notifications).

https://github.com/ivanvermeyen/laravel-google-drive-demo

@MostafaRabia
Copy link

hi, why it doesn't cache photos if i get it from url ?

@leduonggithub
Copy link

hi ivanvermeyen , please help me with my problem. when user click, choose file(s), then click submit, i want store this file(s) directly to google driver, and then save link to database. how can i do that

@npv34
Copy link

npv34 commented Jun 7, 2018

How to show image thumbnail image upload ???

@CarlosMarival
Copy link

how can i get the others attributes like iconLink

@nutch31
Copy link

nutch31 commented Aug 3, 2018

Please advise how to get (teamDriveId) GOOGLE_DRIVE_TEAM_DRIVE_ID .
i need to more learn about this one.

Thank you for your advise .

@afrasiyabhaider
Copy link

How to get downloadable link of a file?

@ivanvermeyen
Copy link
Author

ivanvermeyen commented Oct 20, 2019

Hi,

Please create an issue @ https://github.com/ivanvermeyen/laravel-google-drive-demo for any questions.
This keeps it organized and helps other people to find an answer to similar problems.

Also, make sure you go through open and closed issues first, as your question may already have been asked/answered. 👍

I will be happy to help you out if I can!

https://github.com/ivanvermeyen/laravel-google-drive-demo

@toanVini
Copy link

I have been using Google Drive and Laravel in my project but I had problems downloading files larger than 500 Mb. But the download ends at 500 MB to stop. Is there a way to deal with downloading files that are 1 GB - 5 GB in size? Thank you

@myazidinniam
Copy link

myazidinniam commented Mar 18, 2021

@ivanvermeyen,
New issue, after finishing all steps got error when call 'test' route which should create a file:

(1/1) Google_Service_Exception
{
"error": {
"errors": [
{
"domain": "global",
"reason": "required",
"message": "Login Required",
"locationType": "header",
"location": "Authorization"
}
],
"code": 401,
"message": "Login Required"
}
}

ClientId, Secret, Refresh token are set correctly

You can solve this problem with update your env file on section GOOGLE_REFRESH_TOKEN with separator on value
example GOOGLE_REFRESH_TOKEN="1//04xd08cKBcYDWCgYIARAAGAQSNwF-L9IruTrG9cNMQajASodNAnuoYRPsPWpbJxxxxxxxxxxxx"
another that
Screen Shot 2021-03-18 at 16 05 31

@tranhoandz98
Copy link

how to get body code in file REST.php line 151 to 142.

public static function decodeHttpResponse(
ResponseInterface $response,
RequestInterface $request = null,
$expectedClass = null
) {
$code = $response->getStatusCode();

// retry strategy
if (intVal($code) >= 400) {
  // if we errored out, it should be safe to grab the response body
  $body = (string) $response->getBody();

  // Check if we received errors, and add those to the Exception for convenience
  throw new GoogleServiceException($body, $code, null, self::getResponseErrors($body));
}

// Ensure we only pull the entire body into memory if the request is not
// of media type
$body = self::decodeBody($response, $request);

if ($expectedClass = self::determineExpectedClass($expectedClass, $request)) {
  $json = json_decode($body, true);

  return new $expectedClass($json);
}

return $response;

}

@Gowthamhm
Copy link

In the Git Repo https://github.com/ivanvermeyen/laravel-google-drive-demo , you call the google drive api calls in the web.php(routes) only.
Please give a some example or provide some resources to call the drive storage api call in controllers also.

This repo helped a lot for me, Thanks for creating this repo.
Thank You

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment