Quickstart: запустить приложение на жестком диске в PHP, похоже, использует устаревшую версию [google-api–php-client] [2], также не приводится пример того, как загрузить файл с помощью учетной записи службы.
В моем проекте мне нужно добавить файл (электронную таблицу) с помощью учетной записи службы, добавить электронную таблицу, поделиться электронной таблицей с другим пользователем и добавить строки в электронную таблицу.
Я могу получить пустой список электронных таблиц, но я не мог понять, как загрузить файл, используя учетную запись службы.
Класс PHP google-api-php-client / examples / spreadsheet.php:
<?php use Google\Spreadsheet\DefaultServiceRequest; use Google\Spreadsheet\ServiceRequestFactory; use Google\Spreadsheet\SpreadsheetService; define('JPATH_PLATFORM',1); require_once realpath(dirname(__FILE__) . '/../autoload.php'); class GoogleSpreadSheet { protected $data; protected $client_id = null ; //Client ID protected $service_account_name = null; //Email Address protected $key_file_location = null; //key.p12 protected $client = null; protected $aplication_name = null; protected $_errors = array(); protected $scopes = array( 'https://www.googleapis.com/auth/drive' , 'https://www.googleapis.com/auth/drive.appdata' , 'https://www.googleapis.com/auth/drive.apps.readonly' , 'https://spreadsheets.google.com/feeds' ); /** * Class constructor * @param mixed $properties Either and associative array or another * object to set the initial properties of the object. */ public function __construct($properties = null){ if ($properties !== null) { $this->setProperties($properties); } $this->data = new stdClass(); $this->getClient(); } /** * Get Google_Client object * @return mixed Google_Client object if client ID and application_name are set OR null */ public function getClient(){ if(empty($this->client) AND $this->get('client_id') AND $this->get('aplication_name')){ $this->client = new Google_Client(); $this->client->setApplicationName($this->get('aplication_name')); $this->client->setClientId($this->get('client_id')); } return $this->client; } /** * Get Service AccessToken * @return AccessToken */ public function getToken() { $key = file_get_contents($this->get('key_file_location')); $cred = new Google_Auth_AssertionCredentials( $this->service_account_name, $this->get('scopes', array()), $key ); $this->client->setAssertionCredentials($cred); if($this->client->getAuth()->isAccessTokenExpired()) { $this->client->getAuth()->refreshTokenWithAssertion($cred); } $service_token = json_decode($this->client->getAccessToken()); return $service_token->access_token; } /** * Get Available Spreadsheets */ public function getSpreadsheetsList() { $accessToken = $this->getToken(); $serviceRequest = new DefaultServiceRequest($accessToken); ServiceRequestFactory::setInstance($serviceRequest); $spreadsheetService = new SpreadsheetService(); $spreadsheetFeed = $spreadsheetService->getSpreadsheets(); echo '<pre>'; print_r($spreadsheetFeed); foreach($spreadsheetFeed as $item) { $spreadsheets[basename($item->getId())] = $item->getTitle(); } $this->data->spreadsheets = isset($spreadsheets) ? $spreadsheets : false; } /** * Insert new file. * * @param string $title Title of the file to insert, including the extension. * @param string $description Description of the file to insert. * @param string $parentId Parent folder's ID. * @param string $mimeType MIME type of the file to insert. * @param string $filename Filename of the file to insert. * @return Google_DriveFile The file that was inserted. NULL is returned if an API error occurred. */ public function insertFile($title, $description, $mimeType='application/vnd.google-apps.spreadsheet', $filename=null, $parentId=null) { //Google_DriveService $service Drive API service instance. $service = new Google_Service_Drive($this->getClient()); $file = new Google_Service_Drive_DriveFile(); $file->setTitle($title); $file->setDescription($description); $file->setMimeType($mimeType); // Set the parent folder. if ($parentId != null) { $parent = new Google_ParentReference(); $parent->setId($parentId); $file->setParents(array($parent)); } try { if(!empty($filename)){ $data = file_get_contents($filename); }else{ $data = ''; } $createdFile = $service->files->insert($file, array( 'data' => $data, 'mimeType' => $mimeType, )); // Uncomment the following line to print the File ID // print 'File ID: %s' % $createdFile->getId(); return $createdFile; } catch (Exception $e) { $this->setError($e->getMessage()); print "An error occurred: " . $e->getMessage(); } } /** * Sets a default value if not alreay assigned * @param string $property The name of the property. * @param mixed $default The default value. * @return mixed */ public function def($property, $default = null) { $value = $this->get($property, $default); return $this->set($property, $value); } /** * Returns a property of the object or the default value if the property is not set. * @param string $property The name of the property. * @param mixed $default The default value. * @return mixed The value of the property. */ public function get($property, $default = null) { if (isset($this->$property)) { return $this->$property; } return $default; } /** * Returns an associative array of object properties. * @param boolean $public If true, returns only the public properties. * @return array */ public function getProperties($public = true) { $vars = get_object_vars($this); if ($public) { foreach ($vars as $key => $value) { if ('_' == substr($key, 0, 1)) { unset($vars[$key]); } } } return $vars; } /** * Get the most recent error message. * @param integer $i Option error index. * @param boolean $toString Indicates if JError objects should return their error message. * @return string Error message */ public function getError($i = null, $toString = true) { // Find the error if ($i === null) { // Default, return the last message $error = end($this->_errors); } elseif (!array_key_exists($i, $this->_errors)) { // If $i has been specified but does not exist, return false return false; } else { $error = $this->_errors[$i]; } // Check if only the string is requested if ($error instanceof Exception && $toString) { return (string) $error; } return $error; } /** * Return all errors, if any. * @return array Array of error messages or JErrors. */ public function getErrors() { return $this->_errors; } /** * Modifies a property of the object, creating it if it does not already exist. * @param string $property The name of the property. * @param mixed $value The value of the property to set. * @return mixed Previous value of the property. */ public function set($property, $value = null) { $previous = isset($this->$property) ? $this->$property : null; $this->$property = $value; return $previous; } /** * Set the object properties based on a named array/hash. * @param mixed $properties Either an associative array or another object. * @return boolean */ public function setProperties($properties) { if (is_array($properties) || is_object($properties)) { foreach ((array) $properties as $k => $v) { // Use the set function which might be overridden. $this->set($k, $v); } return true; } return false; } /** * Add an error message. * @param string $error Error message. * @return void */ public function setError($error) { array_push($this->_errors, $error); } } $opt = array(); $opt['key_file_location'] = realpath(dirname(__FILE__)).'/key_ss.p12'; $opt['client_id'] = '*********.apps.googleusercontent.com'; $opt['service_account_name']= '*********@developer.gserviceaccount.com'; $opt['aplication_name'] = 'MY APPLICATION NAME'; $GoogleSS = new GoogleSpreadSheet($opt); $GoogleSS->insertFile('test', 'description'); $GoogleSS->getSpreadsheetsList();
У меня также есть модифицированная версия /google-api-php-client/autoload.php:
<?php function google_api_php_client_autoload($className) { $classPath = explode('_', $className); if ($classPath[0] != 'Google') { return; } if (count($classPath) > 3) { // Maximum class file path depth in this project is 3. $classPath = array_slice($classPath, 0, 3); } $filePath = dirname(__FILE__) . '/src/' . implode('/', $classPath) . '.php'; if (file_exists($filePath)) { require_once($filePath); } } function google_spreadsheet_api_php_client_autoload($className) { $classPath = explode('\\', $className); if (count($classPath) == 1) { $filePath = dirname(__FILE__) . '/src/Google/Spreadsheet/' . $className . '.php'; }else{ $filePath = dirname(__FILE__) . '/src/' . implode('/', $classPath) . '.php'; } if (file_exists($filePath)) { require_once($filePath); } } spl_autoload_register('google_api_php_client_autoload'); spl_autoload_register('google_spreadsheet_api_php_client_autoload');
Когда я выполняю код, я получаю:
Notice: Undefined index: uploadType in /var/www/google-api-php-client/src/Google/Service/Resource.php on line 175 Notice: Undefined index: uploadType in /var/www/google-api-php-client/src/Google/Service/Resource.php on line 175 An error occurred: Error calling POST https://www.googleapis.com/drive/v2/files: (401) Login Required Google\Spreadsheet\SpreadsheetFeed Object ( .... )
Чтобы воспроизвести проблему, вам также потребуется загрузить php-google-электронную таблицу-клиент и загрузить каталог Spreadsheet в / google-api-php-client / src / Google /
Я попытался предоставить как можно больше деталей, но, пожалуйста, дайте мне знать, если что-то неясно.
Я думаю, что ваша проблема – это токен доступа, например: setAccessToken ($ accessToken), проверьте свои учетные данные.
Также я добавил сценарий загрузки Google Drive. Я использую этот скрипт.
С уважением.
<?php /******************************************************************************* * file: google_drive_upload.php * author: ben servoz * description: script to upload files to Google Drive * notes: instructions at http://ben.akrin.com/?p=2080 *******************************************************************************/ // configuration // google oauth $client_id = "your client ID here" ; $client_secret = "your client secret here" ; $refresh_token = "your refresh token here" ; // chunk size (this HAS to be a multiple of 256KB so only change the last integer) $chunk_size = 256 * 1024 * 400 ; // this will upload files 100MB at a time // miscellaneous $verbose = true ; $file_binary = "/usr/bin/file" ; // used for detecting mime type // md5 $check_md5_after_upload = true ; $md5sum_binary = "/usr/bin/md5sum" ; // todo: grant URL follow thingy if it hasn't been done already if( count($argv)<2 || count($argv)>3 || in_array("-h", $argv) || in_array("--help", $argv) ) { echo "usage: {$argv[0]} <file_name> [folder_id]\n\n where <file_name> is the full path to the file that you want to upload to Google Drive.\n and [folder_id] is the the folder where you want to upload the file (optional, defaults to root)\n\n" ; exit( 1 ) ; } $file_name = $argv[1] ; if( !file_exists($file_name) ) { echo "ERROR: {$file_name} is not found on the filesystem\n" ; exit( 1 ) ; } $mime_type = get_mime_type( $file_name ) ; if( $verbose ) { echo " > mime type detected: {$mime_type}\n" ; } $folder_id = "" ; if( count($argv)>2 ) { $folder_id = $argv[2] ; } // retrieving current access token $access_token = get_access_token() ; // we create the file that we will be uploading to with google if( $verbose ) { echo "> creating file with Google\n" ; } $location = create_google_file( $file_name ) ; $file_size = filesize( $file_name ) ; if( $verbose ) { echo "> uploading {$file_name} to {$location}\n" ; } if( $verbose ) { echo "> file size: " . (string)($file_size / pow(1024, 2)) . "MB\n" ; } if( $verbose ) { echo "> chunk size: " . (string)($chunk_size / pow(1024, 2)) . "MB\n\n" ; } $last_response_code = false ; $final_output = null ; $last_range = false ; $transaction_counter = 0 ; $average_upload_speed = 0 ; $do_exponential_backoff = false ; $exponential_backoff_counter = 0 ; while( $last_response_code===false || $last_response_code=='308' ) { $transaction_counter++ ; if( $verbose ) { echo "> request {$transaction_counter}\n" ; } if( $do_exponential_backoff ) { $sleep_for = pow( 2, $exponential_backoff_counter ) ; if( $verbose ) { echo "> exponential backoff kicked in, sleeping for {$sleep_for} and a bit\n" ; } sleep( $sleep_for ) ; usleep( rand(0, 1000) ) ; $exponential_backoff_counter++ ; if( $exponential_backoff_counter>5 ) { // we've waited too long as per Google's instructions echo "ERROR: reached time limit of exponential backoff\n" ; exit( 1 ) ; } } // determining what range is next $range_start = 0 ; $range_end = min( $chunk_size, $file_size - 1 ) ; if( $last_range!==false ) { $last_range = explode( '-', $last_range ) ; $range_start = (int)$last_range[1] + 1 ; $range_end = min( $range_start + $chunk_size, $file_size - 1 ) ; } if( $verbose ) { echo "> range {$range_start}-{$range_end}/{$file_size}\n" ; } $ch = curl_init() ; curl_setopt( $ch, CURLOPT_URL, "{$location}" ) ; curl_setopt( $ch, CURLOPT_PORT , 443 ) ; curl_setopt( $ch, CURLOPT_CUSTOMREQUEST, "PUT" ) ; curl_setopt( $ch, CURLOPT_BINARYTRANSFER, 1 ) ; // grabbing the data to send $to_send = file_get_contents( $file_name, false, NULL, $range_start, ($range_end - $range_start + 1) ) ; curl_setopt( $ch, CURLOPT_POSTFIELDS, $to_send ) ; curl_setopt( $ch, CURLOPT_RETURNTRANSFER, 1) ; curl_setopt( $ch, CURLOPT_HEADER, true ) ; curl_setopt( $ch, CURLOPT_HTTPHEADER, array("Authorization: Bearer {$access_token}", "Content-Length: " . (string)($range_end - $range_start + 1), "Content-Type: {$mime_type}", "Content-Range: bytes {$range_start}-{$range_end}/{$file_size}") ) ; $response = parse_response( curl_exec($ch) ) ; $post_transaction_info = curl_getinfo( $ch ) ; curl_close( $ch ) ; $do_exponential_backoff = false ; if( isset($response['code']) ) { // checking for expired credentials if( $response['code']=="401" ) { // todo: make sure that we also got an invalid credential response if( $verbose ) { echo "> access token expired, getting a new one\n" ; } $access_token = get_access_token( true ) ; $last_response_code = false ; } else if( $response['code']=="308" ) { $last_response_code = $response['code'] ; $last_range = $response['headers']['range'] ; // todo: verify x-range-md5 header to be sure, although I can't seem to find what x-range-md5 is a hash of exactly... $exponential_backoff_counter = 0 ; } else if( $response['code']=="503" ) { // Google's letting us know we should retry $do_exponential_backoff = true ; $last_response_code = false ; } else if( $response['code']=="200" ) { // we are done! $last_response_code = $response['code'] ; $final_output = $response ; } else { echo "ERROR: I have no idea what to do so here's a variable dump & have fun figuring it out.\n" ; echo "post_transaction_info\n" ; print_r( $post_transaction_info ) ; echo "response\n" ; print_r( $response ) ; exit( 1 ) ; } $average_upload_speed += (int)$post_transaction_info['speed_upload'] ; if( $verbose ) { echo "> uploaded {$post_transaction_info['size_upload']}B\n" ; } } else { $do_exponential_backoff = true ; $last_response_code = false ; } } if( $last_response_code!="200" ) { echo "ERROR: there's no way we should reach this point\n" ; exit( 1 ) ; } if( $verbose ) { echo "\n> all done!\n" ; } $average_upload_speed /= $transaction_counter ; if( $verbose ) { echo "\n> average upload speed: " . (string)($average_upload_speed / pow(1024, 2)) . "MB/s\n" ; } $final_output =json_decode( $final_output['body'] ) ; if( $check_md5_after_upload ) { if( $verbose ) { echo "> md5 hash verification " ; } $result = exec( "{$md5sum_binary} {$file_name}" ) ; $result = trim( $result ) ; $result = explode( " ", $result ) ; $result = $result[0] ; if( $result!=$final_output->md5Checksum ) { if( $verbose ) { echo "FAIL\n" ; } echo "ERROR: md5 mismatch; local:{$result}, google:{$final_output->md5Checksum}\n" ; exit( 1 ) ; } else { if( $verbose ) { echo "OK\n" ; } } } echo $final_output->selfLink, "\n" ; // we made it exit( 0 ) ; function get_mime_type( $file_name ) { global $file_binary ; $result = exec( "{$file_binary} -i -b {$file_name}" ) ; $result = trim( $result ) ; $result = explode( ";", $result ) ; $result = $result[0] ; return $result ; } function parse_response( $raw_data ) { $parsed_response = array( 'code'=>-1, 'headers'=>array(), 'body'=>"" ) ; $raw_data = explode( "\r\n", $raw_data ) ; $parsed_response['code'] = explode( " ", $raw_data[0] ) ; $parsed_response['code'] = $parsed_response['code'][1] ; for( $i=1 ; $i<count($raw_data) ; $i++ ) { $raw_datum = $raw_data[$i] ; $raw_datum = trim( $raw_datum ) ; if( $raw_datum!="" ) { if( substr_count($raw_datum, ':')>=1 ) { $raw_datum = explode( ':', $raw_datum, 2 ) ; $parsed_response['headers'][strtolower($raw_datum[0])] = trim( $raw_datum[1] ) ; } else { echo "ERROR: we're in the headers section of parsing an HTTP section and no colon was found for line: {$raw_datum}\n" ; exit( 1 ) ; } } else { // we've moved to the body section if( ($i+1)<count($raw_data) ) { for( $j=($i+1) ; $j<count($raw_data) ; $j++ ) { $parsed_response['body'] .= $raw_data[$j] . "\n" ; } } // we don't need to continue the $i loop break ; } } return $parsed_response ; } function get_access_token( $force_refresh=false ) { global $client_id, $client_secret, $refresh_token, $verbose ; if( $verbose ) { echo "> retrieving access token\n" ; } $token_filename = "/tmp/access_token_" . md5( $client_id . $client_secret . $refresh_token ) ; $access_token = "" ; if( !file_exists($token_filename) || $force_refresh===true ) { // no previous access token, let's get one if( $verbose ) { echo "> getting new one\n" ; } $ch = curl_init() ; curl_setopt( $ch, CURLOPT_URL, "https://accounts.google.com/o/oauth2/token" ) ; curl_setopt( $ch, CURLOPT_PORT , 443 ) ; curl_setopt( $ch, CURLOPT_POST, 1 ) ; curl_setopt( $ch, CURLOPT_POSTFIELDS, "client_id={$client_id}&client_secret={$client_secret}&refresh_token={$refresh_token}&grant_type=refresh_token" ) ; curl_setopt( $ch, CURLOPT_RETURNTRANSFER, 1 ) ; curl_setopt( $ch, CURLOPT_HEADER, true ) ; curl_setopt( $ch, CURLOPT_HTTPHEADER, array('Content-Type: application/x-www-form-urlencoded') ) ; $response = curl_exec( $ch ) ; $response = parse_response( $response ) ; // todo: make sure that we got a valid response before retrieving the access token from it $access_token = json_decode( $response['body'] ) ; $access_token = $access_token->access_token ; file_put_contents( $token_filename, $access_token ) ; } else { // we already have something cached, with some luck it's still valid $access_token = file_get_contents( $token_filename ) ; if( $verbose ) { echo "> from cache\n" ; } } if( $access_token=="" ) { echo "ERROR: problems getting an access token\n" ; exit( 1 ) ; } return $access_token ; } function create_google_file ( $file_name ) { global $access_token, $folder_id, $mime_type ; // todo: make mimeType universal if( $folder_id=="" ) { $post_fields = "{\"title\":\"{$file_name}\", \"mimeType\":\"{$mime_type}\"}" ; } else { $post_fields = "{\"title\":\"{$file_name}\", \"mimeType\":\"{$mime_type}\", \"parents\": [{\"kind\":\"drive#fileLink\",\"id\":\"{$folder_id}\"}]}" ; } $ch = curl_init() ; curl_setopt( $ch, CURLOPT_URL, "https://www.googleapis.com/upload/drive/v2/files?uploadType=resumable" ) ; curl_setopt( $ch, CURLOPT_PORT , 443 ) ; curl_setopt( $ch, CURLOPT_POST, 1 ) ; curl_setopt( $ch, CURLOPT_POSTFIELDS, $post_fields ) ; curl_setopt( $ch, CURLOPT_RETURNTRANSFER, 1 ) ; curl_setopt( $ch, CURLOPT_HEADER, true ) ; curl_setopt( $ch, CURLOPT_HTTPHEADER, array("Authorization: Bearer {$access_token}", "Content-Length: " . strlen($post_fields), "X-Upload-Content-Type: {$mime_type}", "X-Upload-Content-Length: " . filesize($file_name), "Content-Type: application/json; charset=UTF-8") ) ; $response = curl_exec( $ch ) ; $response = parse_response( $response ) ; // access token expired, let's get a new one and try again if( $response['code']=="401" ) { // todo: make sure that we also got an invalid credential response $access_token = get_access_token( true ) ; return create_google_file( $file_name ) ; // todo: implement recursion depth limit so we don't rescurse to hell } // error checking if( $response['code']!="200" ) { echo "ERROR: could not create resumable file\n" ; print_r( $response ) ; exit( 1 ) ; } if( !isset($response['headers']['location']) ) { echo "ERROR: not location header gotten back\n" ; print_r( $response ) ; exit( 1 ) ; } return $response['headers']['location'] ; } ?>
Вы получаете ошибку 401. Таким образом, у вас есть проблемы с вашими учетными данными. Более подробную информацию смотрите в документации по ошибкам . Возможно, ваш токен истек, или он недействителен.