Laravel by default doesn't have ability to use Google Drive as a storage. It means you're trying to utilize some external package. What is it? Did you read it's documentation?
Can you show \App\Services\GoogleDriveService class contents?
Currently I am using the service account and setup the testing route with the credentials and getting this error:
Route::get('/drive-test', function () {
$service = new \App\Services\GoogleDriveService();
$fileId = $service->upload(
storage_path('1770402265.shivam-resume (1).pdf'),
'test.pdf'
);
return $service->getFileUrl($fileId);
});
Google \ Service \ Exception (403)
{ "error": { "code": 403, "message": "Service Accounts do not have storage quota. Leverage shared drives (https://developers.google.com/workspace/drive/api/guides/about-shareddrives), or use OAuth delegation (http://support.google.com/a/answer/7281227) instead.", "errors": [ { "message": "Service Accounts do not have storage quota. Leverage shared drives (https://developers.google.com/workspace/drive/api/guides/about-shareddrives), or use OAuth delegation (http://support.google.com/a/answer/7281227) instead.", "domain": "usageLimits", "reason": "storageQuotaExceeded" } ] } }
Laravel by default doesn't have ability to use Google Drive as a storage. It means you're trying to utilize some external package. What is it? Did you read it's documentation?
Can you show \App\Services\GoogleDriveService class contents?
<?php
namespace App\Services;
use Google\Client;
use Google\Service\Drive;
use Google\Service\Drive\DriveFile;
class GoogleDriveService
{
protected Drive $drive;
protected string $parentFolderId;
public function __construct()
{
$client = new Client();
$client->setClientId(env('GOOGLE_DRIVE_CLIENT_ID'));
$client->setClientSecret(env('GOOGLE_DRIVE_CLIENT_SECRET'));
$client->setRedirectUri(env('GOOGLE_DRIVE_REDIRECT_URI'));
$client->addScope(Drive::DRIVE);
$client->refreshToken(env('GOOGLE_DRIVE_REFRESH_TOKEN'));
$this->drive = new Drive($client);
// Use parent folder from config
$this->parentFolderId = config('drive.parent_folder_id');
}
/**
* Upload a file into a subfolder based on the config mapping
*/
public function uploadByPosition(string $filePath, string $fileName, string $position): array
{
// Get folder name from config mapping, default to position if not mapped
$folderMap = config('drive.position_folders', []);
$folderName = $folderMap[$position] ?? $position;
$subFolderId = $this->getOrCreateSubFolder($folderName);
$fileMetadata = new DriveFile([
'name' => $fileName,
'parents' => [$subFolderId]
]);
$content = file_get_contents($filePath);
$file = $this->drive->files->create($fileMetadata, [
'data' => $content,
'mimeType' => mime_content_type($filePath),
'uploadType' => 'multipart',
'supportsAllDrives' => true,
]);
return [
'subfolder' => $folderName,
'file_id' => $file->id,
'preview_url' => $this->getFileUrl($file->id)
];
}
/**
* Get or create a subfolder under parent
*/
protected function getOrCreateSubFolder(string $folderName): string
{
$response = $this->drive->files->listFiles([
'q' => "name='{$folderName}' and mimeType='application/vnd.google-apps.folder' and '{$this->parentFolderId}' in parents and trashed=false",
'spaces' => 'drive',
'fields' => 'files(id, name)',
'supportsAllDrives' => true,
]);
if (count($response->files) > 0) {
return $response->files[0]->id;
}
// Folder not found → create it
$folderMetadata = new DriveFile([
'name' => $folderName,
'mimeType' => 'application/vnd.google-apps.folder',
'parents' => [$this->parentFolderId],
]);
$folder = $this->drive->files->create($folderMetadata, [
'fields' => 'id',
'supportsAllDrives' => true,
]);
return $folder->id;
}
/**
* Generate a preview URL for Google Drive
*/
public function getFileUrl(string $fileId): string
{
return "https://drive.google.com/file/d/{$fileId}/view";
}
}
Did you write it yourself or got this code somewhere?
Error you get contains links to Google documentation regarding the issue. Most likely account type you created (service account key?) can't be used for accessing Google Drive or something like that.
See how it is recommended to create an account: https://github.com/ivanvermeyen/laravel-google-drive-demo/blob/master/README/1-getting-your-dlient-id-and-secret.md
Also, I'd highly recommend not using env() in the code, rather Config::get() as the former will cause you issues when you cache the config in production.
With the doc and little bit help of ai.
@shivamyadav As @ian_h says, you should not be using the env helper in your code. The Laravel docs also tell you not to do this.
Instead, you should be binding that class in the service container where the configuration values are passed to your constructor:
public function register(): void
{
$this->app->singleton(GoogleDriveService::class, function () {
return new GoogleDriveService(
clientId: $this->app['config']['services.google.drive.client_id'],
clientSecret: $this->app['config']['services.google.drive.client_secret'],
redirectUri: $this->app['config']['services.google.drive.redirect'],
);
});
}
You can then just type-hint the service class, and it will be resolved (and configured) by the service container:
class SomeController extends Controller
{
protected GoogleDriveService $googleDrive;
public function __construct(GoogleDriveService $googleDrive)
{
$this->googleDrive = $googleDrive;
}
}
As for the error, it’s telling you what the problem is: service accounts don’t have a storage quote, so you can’t use service accounts for storage-related operations like you’re trying to do. You need to use an OAuth approach so you’re authenticating as a user, who will have a storage quota. You can use Socialite to create (and refresh) Google access tokens.
Thanks for the info. I did not know about the env direct helper issue in the code.
I have already setup the oauth and using and it works fine now.
In Google Drive (Workspace), create a Shared drive (e.g., “App Uploads”).
Add [email protected] to that Shared Drive with at least Content manager.
Create (or pick) a folder inside the shared drive and copy its folder ID.
Key points:
Authenticate with the service account JSON (not client_id/secret/refresh_token).
Set supportsAllDrives => true.
Parent folder must be in the shared drive.
use Google\Client;
use Google\Service\Drive;
use Google\Service\Drive\DriveFile;
$client = new Client();
$client->setAuthConfig(storage_path('app/google/service-account.json'));
$client->addScope(Drive::DRIVE);
$drive = new Drive($client);
$folderIdInSharedDrive = config('drive.parent_folder_id'); // must be a folder inside the Shared Drive
$fileMetadata = new DriveFile([
'name' => 'test.pdf',
'parents' => [$folderIdInSharedDrive],
]);
$content = file_get_contents(storage_path('1770402265.shivam-resume (1).pdf'));
$file = $drive->files->create($fileMetadata, [
'data' => $content,
'mimeType' => 'application/pdf',
'uploadType' => 'multipart',
'supportsAllDrives' => true,
]);
return $file->id;
If you also search/create subfolders, include shared-drive flags there too (you already have supportsAllDrives => true). When listing folders in shared drives, you often also need:
includeItemsFromAllDrives => true
corpora => 'drive'
driveId => '{SHARED_DRIVE_ID}'
Shared drives are explicitly the recommended path for service accounts JSYK.
so your code should be looking like this
<?php
namespace App\Services;
use Google\Client;
use Google\Service\Drive;
use Google\Service\Drive\DriveFile;
class GoogleDriveService
{
protected Drive $drive;
protected string $parentFolderId;
protected string $sharedDriveId;
public function __construct()
{
$client = new Client();
$client->setAuthConfig(storage_path('app/google/service-account.json'));
$client->addScope(Drive::DRIVE);
$this->drive = new Drive($client);
$this->parentFolderId = config('drive.parent_folder_id');
$this->sharedDriveId = config('drive.shared_drive_id');
}
/**
* Upload a file into a subfolder based on the config mapping
*/
public function uploadByPosition(string $filePath, string $fileName, string $position): array
{
$folderMap = config('drive.position_folders', []);
$folderName = $folderMap[$position] ?? $position;
$subFolderId = $this->getOrCreateSubFolder($folderName);
$fileMetadata = new DriveFile([
'name' => $fileName,
'parents' => [$subFolderId],
]);
$content = file_get_contents($filePath);
$file = $this->drive->files->create($fileMetadata, [
'data' => $content,
'mimeType' => mime_content_type($filePath),
'uploadType' => 'multipart',
'supportsAllDrives' => true,
]);
return [
'subfolder' => $folderName,
'file_id' => $file->id,
'preview_url' => $this->getFileUrl($file->id),
];
}
/**
* Simple upload (no position/subfolder logic)
*/
public function upload(string $filePath, string $fileName): string
{
$fileMetadata = new DriveFile([
'name' => $fileName,
'parents' => [$this->parentFolderId],
]);
$content = file_get_contents($filePath);
$file = $this->drive->files->create($fileMetadata, [
'data' => $content,
'mimeType' => mime_content_type($filePath),
'uploadType' => 'multipart',
'supportsAllDrives' => true,
]);
return $file->id;
}
/**
* Get or create a subfolder under parent
*/
protected function getOrCreateSubFolder(string $folderName): string
{
$response = $this->drive->files->listFiles([
'q' => "name='{$folderName}' and mimeType='application/vnd.google-apps.folder' and '{$this->parentFolderId}' in parents and trashed=false",
'spaces' => 'drive',
'fields' => 'files(id, name)',
'supportsAllDrives' => true,
'includeItemsFromAllDrives' => true,
'corpora' => 'drive',
'driveId' => $this->sharedDriveId,
]);
if (count($response->files) > 0) {
return $response->files[0]->id;
}
$folderMetadata = new DriveFile([
'name' => $folderName,
'mimeType' => 'application/vnd.google-apps.folder',
'parents' => [$this->parentFolderId],
]);
$folder = $this->drive->files->create($folderMetadata, [
'fields' => 'id',
'supportsAllDrives' => true,
]);
return $folder->id;
}
/**
* Generate a preview URL for Google Drive
*/
public function getFileUrl(string $fileId): string
{
return "https://drive.google.com/file/d/{$fileId}/view";
}
}
and your config
<?php
return [
'shared_drive_id' => env('GOOGLE_SHARED_DRIVE_ID'), // ID of the Shared Drive itself
'parent_folder_id' => env('GOOGLE_DRIVE_FOLDER_ID'), // ID of a folder inside that Shared Drive
'position_folders' => [
'developer' => 'Developer Resumes',
'designer' => 'Designer Resumes',
'manager' => 'Manager Resumes',
// add more mappings as needed
],
];
Please or to participate in this conversation.