Advertisement
claukiller

Untitled

Apr 27th, 2018
135
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 34.84 KB | None | 0 0
  1. <?php
  2. namespace Hypweb\Flysystem\GoogleDrive;
  3.  
  4. use Google_Service_Drive;
  5. use Google_Service_Drive_DriveFile;
  6. use Google_Service_Drive_FileList;
  7. use Google_Service_Drive_Permission;
  8. use Google_Http_MediaFileUpload;
  9. use League\Flysystem\Adapter\AbstractAdapter;
  10. use League\Flysystem\AdapterInterface;
  11. use League\Flysystem\Config;
  12. use League\Flysystem\Util;
  13.  
  14. class GoogleDriveAdapter extends AbstractAdapter
  15. {
  16.  
  17. /**
  18. * Fetch fields setting for list
  19. *
  20. * @var string
  21. */
  22. const FETCHFIELDS_LIST = 'files(id,mimeType,modifiedTime,name,parents,permissions,size,webContentLink,webViewLink,modifiedTime,size),nextPageToken';
  23.  
  24. /**
  25. * Fetch fields setting for get
  26. *
  27. * @var string
  28. */
  29. const FETCHFIELDS_GET = 'id,name,mimeType,modifiedTime,parents,permissions,size,webContentLink,webViewLink,modifiedTime,size';
  30.  
  31. /**
  32. * MIME tyoe of directory
  33. *
  34. * @var string
  35. */
  36. const DIRMIME = 'application/vnd.google-apps.folder';
  37.  
  38. /**
  39. * Google_Service_Drive instance
  40. *
  41. * @var Google_Service_Drive
  42. */
  43. protected $service;
  44.  
  45. /**
  46. * Default options
  47. *
  48. * @var array
  49. */
  50. protected static $defaultOptions = [
  51. 'spaces' => 'drive',
  52. 'useHasDir' => false,
  53. 'publishPermission' => [
  54. 'type' => 'anyone',
  55. 'role' => 'reader',
  56. 'withLink' => true
  57. ],
  58. 'appsExportMap' => [
  59. 'application/vnd.google-apps.document' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
  60. 'application/vnd.google-apps.spreadsheet' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
  61. 'application/vnd.google-apps.drawing' => 'application/pdf',
  62. 'application/vnd.google-apps.presentation' => 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
  63. 'application/vnd.google-apps.script' => 'application/vnd.google-apps.script+json',
  64. 'default' => 'application/pdf'
  65. ]
  66. ];
  67.  
  68. /**
  69. * A comma-separated list of spaces to query
  70. * Supported values are 'drive', 'appDataFolder' and 'photos'
  71. *
  72. * @var string
  73. */
  74. protected $spaces;
  75.  
  76. /**
  77. * Permission array as published item
  78. *
  79. * @var array
  80. */
  81. protected $publishPermission;
  82.  
  83. /**
  84. * Cache of file objects
  85. *
  86. * @var array
  87. */
  88. private $cacheFileObjects = [];
  89.  
  90. /**
  91. * Cache of hasDir
  92. *
  93. * @var array
  94. */
  95. private $cacheHasDirs = [];
  96.  
  97. /**
  98. * Use hasDir function
  99. *
  100. * @var bool
  101. */
  102. private $useHasDir = false;
  103.  
  104. /**
  105. * Options array
  106. *
  107. * @var array
  108. */
  109. private $options = [];
  110.  
  111. public function __construct(Google_Service_Drive $service, $root = null, $options = [])
  112. {
  113. if (! $root) {
  114. $root = 'root';
  115. }
  116. $this->service = $service;
  117. $this->setPathPrefix($root);
  118. $this->root = $root;
  119.  
  120. $this->options = array_replace_recursive(static::$defaultOptions, $options);
  121.  
  122. $this->spaces = $this->options['spaces'];
  123. $this->useHasDir = $this->options['useHasDir'];
  124. $this->publishPermission = $this->options['publishPermission'];
  125. }
  126.  
  127. /**
  128. * Write a new file.
  129. *
  130. * @param string $path
  131. * @param string $contents
  132. * @param Config $config
  133. * Config object
  134. *
  135. * @return array|false false on failure file meta data on success
  136. */
  137. public function write($path, $contents, Config $config)
  138. {
  139. return $this->upload($path, $contents, $config);
  140. }
  141.  
  142. /**
  143. * Write a new file using a stream.
  144. *
  145. * @param string $path
  146. * @param resource $resource
  147. * @param Config $config
  148. * Config object
  149. *
  150. * @return array|false false on failure file meta data on success
  151. */
  152. public function writeStream($path, $resource, Config $config)
  153. {
  154. return $this->write($path, $resource, $config);
  155. }
  156.  
  157. /**
  158. * Update a file.
  159. *
  160. * @param string $path
  161. * @param string $contents
  162. * @param Config $config
  163. * Config object
  164. *
  165. * @return array|false false on failure file meta data on success
  166. */
  167. public function update($path, $contents, Config $config)
  168. {
  169. return $this->write($path, $contents, $config);
  170. }
  171.  
  172. /**
  173. * Update a file using a stream.
  174. *
  175. * @param string $path
  176. * @param resource $resource
  177. * @param Config $config
  178. * Config object
  179. *
  180. * @return array|false false on failure file meta data on success
  181. */
  182. public function updateStream($path, $resource, Config $config)
  183. {
  184. return $this->write($path, $resource, $config);
  185. }
  186.  
  187. /**
  188. * Rename a file.
  189. *
  190. * @param string $path
  191. * @param string $newpath
  192. *
  193. * @return bool
  194. */
  195. public function rename($path, $newpath)
  196. {
  197. list ($oldParent, $fileId) = $this->splitPath($path);
  198. list ($newParent, $newName) = $this->splitPath($newpath);
  199.  
  200. $file = new Google_Service_Drive_DriveFile();
  201. $file->setName($newName);
  202. $opts = [
  203. 'fields' => self::FETCHFIELDS_GET
  204. ];
  205. if ($newParent !== $oldParent) {
  206. $opts['addParents'] = $newParent;
  207. $opts['removeParents'] = $oldParent;
  208. }
  209.  
  210. $updatedFile = $this->service->files->update($fileId, $file, $opts);
  211.  
  212. if ($updatedFile) {
  213. $this->cacheFileObjects[$updatedFile->getId()] = $updatedFile;
  214. $this->cacheFileObjects[$newName] = $updatedFile;
  215. return true;
  216. }
  217.  
  218. return false;
  219. }
  220.  
  221. /**
  222. * Copy a file.
  223. *
  224. * @param string $path
  225. * @param string $newpath
  226. *
  227. * @return bool
  228. */
  229. public function copy($path, $newpath)
  230. {
  231. list (, $srcId) = $this->splitPath($path);
  232.  
  233. list ($newParentId, $fileName) = $this->splitPath($newpath);
  234.  
  235. $file = new Google_Service_Drive_DriveFile();
  236. $file->setName($fileName);
  237. $file->setParents([
  238. $newParentId
  239. ]);
  240.  
  241. $newFile = $this->service->files->copy($srcId, $file, [
  242. 'fields' => self::FETCHFIELDS_GET
  243. ]);
  244.  
  245. if ($newFile instanceof Google_Service_Drive_DriveFile) {
  246. $this->cacheFileObjects[$newFile->getId()] = $newFile;
  247. $this->cacheFileObjects[$fileName] = $newFile;
  248. list ($newDir) = $this->splitPath($newpath);
  249. $newpath = (($newDir === $this->root) ? '' : ($newDir . '/')) . $newFile->getId();
  250. if ($this->getRawVisibility($path) === AdapterInterface::VISIBILITY_PUBLIC) {
  251. $this->publish($newpath);
  252. } else {
  253. $this->unPublish($newpath);
  254. }
  255. return true;
  256. }
  257.  
  258. return false;
  259. }
  260.  
  261. /**
  262. * Delete a file.
  263. *
  264. * @param string $path
  265. *
  266. * @return bool
  267. */
  268. public function delete($path)
  269. {
  270. if ($file = $this->getFileObject($path)) {
  271. list ($parentId, $id) = $this->splitPath($path);
  272. if ($parents = $file->getParents()) {
  273. $file = new Google_Service_Drive_DriveFile();
  274. $opts = [];
  275. if (count($parents) > 1) {
  276. $opts['removeParents'] = $parentId;
  277. } else {
  278. $file->setTrashed(true);
  279. }
  280. if ($this->service->files->update($id, $file, $opts)) {
  281. unset($this->cacheFileObjects[$id], $this->cacheHasDirs[$id]);
  282. return true;
  283. }
  284. }
  285. }
  286. return false;
  287. }
  288.  
  289. /**
  290. * Delete a directory.
  291. *
  292. * @param string $dirname
  293. *
  294. * @return bool
  295. */
  296. public function deleteDir($dirname)
  297. {
  298. return $this->delete($dirname);
  299. }
  300.  
  301. /**
  302. * Create a directory.
  303. *
  304. * @param string $dirname
  305. * directory name
  306. * @param Config $config
  307. *
  308. * @return array|false
  309. */
  310. public function createDir($dirname, Config $config)
  311. {
  312. list ($pdirId, $name) = $this->splitPath($dirname);
  313.  
  314. $folder = $this->createDirectory($name, $pdirId);
  315. if ($folder) {
  316. $itemId = $folder->getId();
  317. $this->cacheFileObjects[$name] = $folder; // for confirmation by getMetaData() oe has() while in this connection
  318. $this->cacheFileObjects[$itemId] = $folder;
  319. $this->cacheHasDirs[$itemId] = false;
  320. $path_parts = $this->splitFileExtension($name);
  321. $result = [
  322. 'path' => Util::dirname($dirname) . '/' . $itemId,
  323. 'filename' => $path_parts['filename'],
  324. 'extension' => $path_parts['extension']
  325. ];
  326. return $result;
  327. }
  328.  
  329. return false;
  330. }
  331.  
  332. /**
  333. * Check whether a file exists.
  334. *
  335. * @param string $path
  336. *
  337. * @return array|bool|null
  338. */
  339. public function has($path)
  340. {
  341. return ($this->getFileObject($path, true) instanceof Google_Service_Drive_DriveFile);
  342. }
  343.  
  344. /**
  345. * Read a file.
  346. *
  347. * @param string $path
  348. *
  349. * @return array|false
  350. */
  351. public function read($path)
  352. {
  353. list (, $fileId) = $this->splitPath($path);
  354. if ($response = $this->service->files->get($fileId, [
  355. 'alt' => 'media'
  356. ])) {
  357. return [
  358. 'contents' => (string) $response->getBody()
  359. ];
  360. }
  361.  
  362. return false;
  363. }
  364.  
  365. /**
  366. * Read a file as a stream.
  367. *
  368. * @param string $path
  369. *
  370. * @return array|false
  371. */
  372. public function readStream($path)
  373. {
  374. $redirect = [];
  375. if (func_num_args() > 1) {
  376. $redirect = func_get_arg(1);
  377. }
  378. if (! $redirect) {
  379. $redirect = [
  380. 'cnt' => 0,
  381. 'url' => '',
  382. 'token' => '',
  383. 'cookies' => []
  384. ];
  385. if ($file = $this->getFileObject($path)) {
  386. $dlurl = $this->getDownloadUrl($file);
  387. $client = $this->service->getClient();
  388. $token = $client->getAccessToken();
  389. $access_token = '';
  390. if (is_array($token)) {
  391. $access_token = $token['access_token'];
  392. } else {
  393. if ($token = @json_decode($client->getAccessToken())) {
  394. $access_token = $token->access_token;
  395. }
  396. }
  397. $redirect = [
  398. 'cnt' => 0,
  399. 'url' => '',
  400. 'token' => $access_token,
  401. 'cookies' => []
  402. ];
  403. }
  404. } else {
  405. if ($redirect['cnt'] > 5) {
  406. return false;
  407. }
  408. $dlurl = $redirect['url'];
  409. $redirect['url'] = '';
  410. $access_token = $redirect['token'];
  411. }
  412.  
  413. if ($dlurl) {
  414. $url = parse_url($dlurl);
  415. $cookies = [];
  416. if ($redirect['cookies']) {
  417. foreach ($redirect['cookies'] as $d => $c) {
  418. if (strpos($url['host'], $d) !== false) {
  419. $cookies[] = $c;
  420. }
  421. }
  422. }
  423. if ($access_token) {
  424. $query = isset($url['query']) ? '?' . $url['query'] : '';
  425. $stream = stream_socket_client('ssl://' . $url['host'] . ':443');
  426. stream_set_timeout($stream, 300);
  427. fputs($stream, "GET {$url['path']}{$query} HTTP/1.1\r\n");
  428. fputs($stream, "Host: {$url['host']}\r\n");
  429. fputs($stream, "Authorization: Bearer {$access_token}\r\n");
  430. fputs($stream, "Connection: Close\r\n");
  431. if ($cookies) {
  432. fputs($stream, "Cookie: " . join('; ', $cookies) . "\r\n");
  433. }
  434. fputs($stream, "\r\n");
  435. while (($res = trim(fgets($stream))) !== '') {
  436. // find redirect
  437. if (preg_match('/^Location: (.+)$/', $res, $m)) {
  438. $redirect['url'] = $m[1];
  439. }
  440. // fetch cookie
  441. if (strpos($res, 'Set-Cookie:') === 0) {
  442. $domain = $url['host'];
  443. if (preg_match('/^Set-Cookie:(.+)(?:domain=\s*([^ ;]+))?/i', $res, $c1)) {
  444. if (! empty($c1[2])) {
  445. $domain = trim($c1[2]);
  446. }
  447. if (preg_match('/([^ ]+=[^;]+)/', $c1[1], $c2)) {
  448. $redirect['cookies'][$domain] = $c2[1];
  449. }
  450. }
  451. }
  452. }
  453. if ($redirect['url']) {
  454. $redirect['cnt'] ++;
  455. fclose($stream);
  456. return $this->readStream($path, $redirect);
  457. }
  458. return compact('stream');
  459. }
  460. }
  461.  
  462. return false;
  463. }
  464.  
  465. /**
  466. * List contents of a directory.
  467. *
  468. * @param string $dirname
  469. * @param bool $recursive
  470. *
  471. * @return array
  472. */
  473. public function listContents($dirname = '', $recursive = false)
  474. {
  475. return array_values($this->getItems($dirname, $recursive));
  476. }
  477.  
  478. public function listFolderContents($id)
  479. {
  480. return $service->files->list(array('q' => "'$id' in parents"));
  481. }
  482.  
  483. /**
  484. * Get all the meta data of a file or directory.
  485. *
  486. * @param string $path
  487. *
  488. * @return array|false
  489. */
  490. public function getMetadata($path)
  491. {
  492. if ($obj = $this->getFileObject($path, true)) {
  493. if ($obj instanceof Google_Service_Drive_DriveFile) {
  494. return $this->normaliseObject($obj, Util::dirname($path));
  495. }
  496. }
  497. return false;
  498. }
  499.  
  500. /**
  501. * Get all the meta data of a file or directory.
  502. *
  503. * @param string $path
  504. *
  505. * @return array|false
  506. */
  507. public function getSize($path)
  508. {
  509. $meta = $this->getMetadata($path);
  510. return ($meta && isset($meta['size'])) ? $meta : false;
  511. }
  512.  
  513. /**
  514. * Get the mimetype of a file.
  515. *
  516. * @param string $path
  517. *
  518. * @return array|false
  519. */
  520. public function getMimetype($path)
  521. {
  522. $meta = $this->getMetadata($path);
  523. return ($meta && isset($meta['mimetype'])) ? $meta : false;
  524. }
  525.  
  526. /**
  527. * Get the timestamp of a file.
  528. *
  529. * @param string $path
  530. *
  531. * @return array|false
  532. */
  533. public function getTimestamp($path)
  534. {
  535. $meta = $this->getMetadata($path);
  536. return ($meta && isset($meta['timestamp'])) ? $meta : false;
  537. }
  538.  
  539. /**
  540. * Set the visibility for a file.
  541. *
  542. * @param string $path
  543. * @param string $visibility
  544. *
  545. * @return array|false file meta data
  546. */
  547. public function setVisibility($path, $visibility)
  548. {
  549. $result = ($visibility === AdapterInterface::VISIBILITY_PUBLIC) ? $this->publish($path) : $this->unPublish($path);
  550.  
  551. if ($result) {
  552. return compact('path', 'visibility');
  553. }
  554.  
  555. return false;
  556. }
  557.  
  558. /**
  559. * Get the visibility of a file.
  560. *
  561. * @param string $path
  562. *
  563. * @return array|false
  564. */
  565. public function getVisibility($path)
  566. {
  567. return [
  568. 'visibility' => $this->getRawVisibility($path)
  569. ];
  570. }
  571.  
  572. // /////////////////- ORIGINAL METHODS -///////////////////
  573.  
  574. /**
  575. * Get contents parmanent URL
  576. *
  577. * @param string $path
  578. * itemId path
  579. *
  580. * @return string|false
  581. */
  582. public function getUrl($path)
  583. {
  584. if ($this->publish($path)) {
  585. $obj = $this->getFileObject($path);
  586. if ($url = $obj->getWebContentLink()) {
  587. return str_replace('export=download', 'export=media', $url);
  588. }
  589. if ($url = $obj->getWebViewLink()) {
  590. return $url;
  591. }
  592. }
  593. return false;
  594. }
  595.  
  596. /**
  597. * Has child directory
  598. *
  599. * @param string $path
  600. * itemId path
  601. *
  602. * @return array
  603. */
  604. public function hasDir($path)
  605. {
  606. $meta = $this->getMetadata($path);
  607. return ($meta && isset($meta['hasdir'])) ? $meta : [
  608. 'hasdir' => true
  609. ];
  610. }
  611.  
  612. /**
  613. * Do cache cacheHasDirs with batch request
  614. *
  615. * @param array $targets
  616. * [[path => id],...]
  617. *
  618. * @return void
  619. */
  620. protected function setHasDir($targets, $object)
  621. {
  622. $service = $this->service;
  623. $client = $service->getClient();
  624. $gFiles = $service->files;
  625. $opts = [
  626. 'pageSize' => 1
  627. ];
  628. $paths = [];
  629. $client->setUseBatch(true);
  630. $batch = $service->createBatch();
  631. $i = 0;
  632. foreach ($targets as $id) {
  633. $opts['q'] = sprintf('trashed = false and "%s" in parents and mimeType = "%s"', $id, self::DIRMIME);
  634. $request = $gFiles->listFiles($opts);
  635. $key = ++ $i;
  636. $batch->add($request, (string) $key);
  637. $paths['response-' . $key] = $id;
  638. }
  639. $results = $batch->execute();
  640. foreach ($results as $key => $result) {
  641. if ($result instanceof Google_Service_Drive_FileList) {
  642. $object[$paths[$key]]['hasdir'] = $this->cacheHasDirs[$paths[$key]] = (bool) $result->getFiles();
  643. }
  644. }
  645. $client->setUseBatch(false);
  646. return $object;
  647. }
  648.  
  649. /**
  650. * Get the object permissions presented as a visibility.
  651. *
  652. * @param string $path
  653. * itemId path
  654. *
  655. * @return string
  656. */
  657. protected function getRawVisibility($path)
  658. {
  659. $file = $this->getFileObject($path);
  660. $permissions = $file->getPermissions();
  661. $visibility = AdapterInterface::VISIBILITY_PRIVATE;
  662. foreach ($permissions as $permission) {
  663. if ($permission->type === $this->publishPermission['type'] && $permission->role === $this->publishPermission['role']) {
  664. $visibility = AdapterInterface::VISIBILITY_PUBLIC;
  665. break;
  666. }
  667. }
  668. return $visibility;
  669. }
  670.  
  671. /**
  672. * Publish specified path item
  673. *
  674. * @param string $path
  675. * itemId path
  676. *
  677. * @return bool
  678. */
  679. protected function publish($path)
  680. {
  681. if (($file = $this->getFileObject($path))) {
  682. if ($this->getRawVisibility($path) === AdapterInterface::VISIBILITY_PUBLIC) {
  683. return true;
  684. }
  685. try {
  686. $permission = new Google_Service_Drive_Permission($this->publishPermission);
  687. if ($this->service->permissions->create($file->getId(), $permission)) {
  688. return true;
  689. }
  690. } catch (Exception $e) {
  691. return false;
  692. }
  693. }
  694.  
  695. return false;
  696. }
  697.  
  698. /**
  699. * Un-publish specified path item
  700. *
  701. * @param string $path
  702. * itemId path
  703. *
  704. * @return bool
  705. */
  706. protected function unPublish($path)
  707. {
  708. if (($file = $this->getFileObject($path))) {
  709. $permissions = $file->getPermissions();
  710. try {
  711. foreach ($permissions as $permission) {
  712. if ($permission->type === 'anyone' && $permission->role === 'reader') {
  713. $this->service->permissions->delete($file->getId(), $permission->getId());
  714. }
  715. }
  716. return true;
  717. } catch (Exception $e) {
  718. return false;
  719. }
  720. }
  721.  
  722. return false;
  723. }
  724.  
  725. /**
  726. * Path splits to dirId, fileId or newName
  727. *
  728. * @param string $path
  729. *
  730. * @return array [ $dirId , $fileId|newName ]
  731. */
  732. protected function splitPath($path, $getParentId = true)
  733. {
  734. if ($path === '' || $path === '/') {
  735. $fileName = $this->root;
  736. $dirName = '';
  737. } else {
  738. $paths = explode('/', $path);
  739. $fileName = array_pop($paths);
  740. if ($getParentId) {
  741. $dirName = $paths ? array_pop($paths) : '';
  742. } else {
  743. $dirName = join('/', $paths);
  744. }
  745. if ($dirName === '') {
  746. $dirName = $this->root;
  747. }
  748. }
  749. return [
  750. $dirName,
  751. $fileName
  752. ];
  753. }
  754.  
  755. /**
  756. * Item name splits to filename and extension
  757. * This function supported include '/' in item name
  758. *
  759. * @param string $name
  760. *
  761. * @return array [ 'filename' => $filename , 'extension' => $extension ]
  762. */
  763. protected function splitFileExtension($name)
  764. {
  765. $filename = $extension = '';
  766. $name_parts = explode('.', $name);
  767. if (isset($name_parts[1])) {
  768. $extension = array_pop($name_parts);
  769. }
  770. $filename = join('.', $name_parts);
  771. return compact('filename', 'extension');
  772. }
  773.  
  774. /**
  775. * Get normalised files array from Google_Service_Drive_DriveFile
  776. *
  777. * @param Google_Service_Drive_DriveFile $object
  778. * @param String $dirname
  779. * Parent directory itemId path
  780. *
  781. * @return array Normalised files array
  782. */
  783. protected function normaliseObject(Google_Service_Drive_DriveFile $object, $dirname)
  784. {
  785. $id = $object->getId();
  786. $path_parts = $this->splitFileExtension($object->getName());
  787. $result = [];
  788. $result['type'] = $object->mimeType === self::DIRMIME ? 'dir' : 'file';
  789. $result['id'] = $object->getId();
  790. $result['path'] = ($dirname ? ($dirname . '/') : '') . $id;
  791. $result['filename'] = $path_parts['filename'];
  792. $result['webViewLink'] = $object['webViewLink'];
  793. $result['size'] = $object['size'];
  794. $result['modifiedTime'] = $object['modifiedTime'];
  795. $result['extension'] = $path_parts['extension'];
  796. $result['timestamp'] = strtotime($object->getModifiedTime());
  797. if ($result['type'] === 'file') {
  798. $result['mimetype'] = $object->mimeType;
  799. $result['size'] = (int) $object->getSize();
  800. }
  801. if ($result['type'] === 'dir') {
  802. $result['size'] = 0;
  803. if ($this->useHasDir) {
  804. $result['hasdir'] = isset($this->cacheHasDirs[$id]) ? $this->cacheHasDirs[$id] : false;
  805. }
  806. }
  807. return $result;
  808. }
  809.  
  810. /**
  811. * Get items array of target dirctory
  812. *
  813. * @param string $dirname
  814. * itemId path
  815. * @param bool $recursive
  816. * @param number $maxResults
  817. * @param string $query
  818. *
  819. * @return array Items array
  820. */
  821. protected function getItems($dirname, $recursive = false, $maxResults = 0, $query = '')
  822. {
  823. list (, $itemId) = $this->splitPath($dirname);
  824.  
  825. $maxResults = min($maxResults, 1000);
  826. $results = [];
  827. $parameters = [
  828. 'pageSize' => $maxResults ?: 1000,
  829. 'fields' => self::FETCHFIELDS_LIST,
  830. 'spaces' => $this->spaces,
  831. 'q' => sprintf('trashed = false and "%s" in parents', $itemId)
  832. ];
  833. if ($query) {
  834. $parameters['q'] .= ' and (' . $query . ')';
  835. ;
  836. }
  837. $pageToken = NULL;
  838. $gFiles = $this->service->files;
  839. $this->cacheHasDirs[$itemId] = false;
  840. $setHasDir = [];
  841.  
  842. do {
  843. try {
  844. if ($pageToken) {
  845. $parameters['pageToken'] = $pageToken;
  846. }
  847. $fileObjs = $gFiles->listFiles($parameters);
  848. if ($fileObjs instanceof Google_Service_Drive_FileList) {
  849. foreach ($fileObjs as $obj) {
  850. $id = $obj->getId();
  851. $this->cacheFileObjects[$id] = $obj;
  852. $result = $this->normaliseObject($obj, $dirname);
  853. $results[$id] = $result;
  854. if ($result['type'] === 'dir') {
  855. if ($this->useHasDir) {
  856. $setHasDir[$id] = $id;
  857. }
  858. if ($this->cacheHasDirs[$itemId] === false) {
  859. $this->cacheHasDirs[$itemId] = true;
  860. unset($setHasDir[$itemId]);
  861. }
  862. if ($recursive) {
  863. $results = array_merge($results, $this->getItems($result['path'], true, $maxResults, $query));
  864. }
  865. }
  866. }
  867. $pageToken = $fileObjs->getNextPageToken();
  868. } else {
  869. $pageToken = NULL;
  870. }
  871. } catch (Exception $e) {
  872. $pageToken = NULL;
  873. }
  874. } while ($pageToken && $maxResults === 0);
  875.  
  876. if ($setHasDir) {
  877. $results = $this->setHasDir($setHasDir, $results);
  878. }
  879. return array_values($results);
  880. }
  881.  
  882. /**
  883. * Get file oblect Google_Service_Drive_DriveFile
  884. *
  885. * @param string $path
  886. * itemId path
  887. * @param string $checkDir
  888. * do check hasdir
  889. *
  890. * @return Google_Service_Drive_DriveFile|null
  891. */
  892. protected function getFileObject($path, $checkDir = false)
  893. {
  894. list (, $itemId) = $this->splitPath($path);
  895. if (isset($this->cacheFileObjects[$itemId])) {
  896. return $this->cacheFileObjects[$itemId];
  897. }
  898.  
  899. $service = $this->service;
  900. $client = $service->getClient();
  901.  
  902. $client->setUseBatch(true);
  903. $batch = $service->createBatch();
  904.  
  905. $opts = [
  906. 'fields' => self::FETCHFIELDS_GET
  907. ];
  908.  
  909. $batch->add($this->service->files->get($itemId, $opts), 'obj');
  910. if ($checkDir && $this->useHasDir) {
  911. $batch->add($service->files->listFiles([
  912. 'pageSize' => 1,
  913. 'q' => sprintf('trashed = false and "%s" in parents and mimeType = "%s"', $itemId, self::DIRMIME)
  914. ]), 'hasdir');
  915. }
  916. $results = array_values($batch->execute());
  917.  
  918. list ($fileObj, $hasdir) = array_pad($results, 2, null);
  919. $client->setUseBatch(false);
  920.  
  921. if ($fileObj instanceof Google_Service_Drive_DriveFile) {
  922. if ($hasdir && $fileObj->mimeType === self::DIRMIME) {
  923. if ($hasdir instanceof Google_Service_Drive_FileList) {
  924. $this->cacheHasDirs[$fileObj->getId()] = (bool) $hasdir->getFiles();
  925. }
  926. }
  927. } else {
  928. $fileObj = NULL;
  929. }
  930. $this->cacheFileObjects[$itemId] = $fileObj;
  931.  
  932. return $fileObj;
  933. }
  934.  
  935. /**
  936. * Get download url
  937. *
  938. * @param Google_Service_Drive_DriveFile $file
  939. *
  940. * @return string|false
  941. */
  942. protected function getDownloadUrl($file)
  943. {
  944. if (strpos($file->mimeType, 'application/vnd.google-apps') !== 0) {
  945. return 'https://www.googleapis.com/drive/v3/files/' . $file->getId() . '?alt=media';
  946. } else {
  947. $mimeMap = $this->options['appsExportMap'];
  948. if (isset($mimeMap[$file->getMimeType()])) {
  949. $mime = $mimeMap[$file->getMimeType()];
  950. } else {
  951. $mime = $mimeMap['default'];
  952. }
  953. $mime = rawurlencode($mime);
  954.  
  955. return 'https://www.googleapis.com/drive/v3/files/' . $file->getId() . '/export?mimeType=' . $mime;
  956. }
  957.  
  958. return false;
  959. }
  960.  
  961. /**
  962. * Create dirctory
  963. *
  964. * @param string $name
  965. * @param string $parentId
  966. *
  967. * @return Google_Service_Drive_DriveFile|NULL
  968. */
  969. protected function createDirectory($name, $parentId)
  970. {
  971. $file = new Google_Service_Drive_DriveFile();
  972. $file->setName($name);
  973. $file->setParents([
  974. $parentId
  975. ]);
  976. $file->setMimeType(self::DIRMIME);
  977.  
  978. $obj = $this->service->files->create($file, [
  979. 'fields' => self::FETCHFIELDS_GET
  980. ]);
  981.  
  982. return ($obj instanceof Google_Service_Drive_DriveFile) ? $obj : false;
  983. }
  984.  
  985. /**
  986. * Upload|Update item
  987. *
  988. * @param string $path
  989. * @param string|resource $contents
  990. * @param Config $config
  991. *
  992. * @return array|false item info array
  993. */
  994. protected function upload($path, $contents, Config $config)
  995. {
  996. list ($parentId, $fileName) = $this->splitPath($path);
  997. $mode = 'update';
  998. $mime = $config->get('mimetype');
  999.  
  1000. $srcFile = $this->getFileObject($path);
  1001. $file = new Google_Service_Drive_DriveFile();
  1002. if (! $srcFile) {
  1003. $mode = 'insert';
  1004. $file->setName($fileName);
  1005. $file->setParents([
  1006. $parentId
  1007. ]);
  1008. }
  1009.  
  1010. $isResource = false;
  1011. if (is_resource($contents)) {
  1012. $fstat = @fstat($contents);
  1013. if (! empty($fstat['size'])) {
  1014. $isResource = true;
  1015. }
  1016. if (! $isResource) {
  1017. $contents = stream_get_contents($contents);
  1018. }
  1019. }
  1020.  
  1021. if ($isResource) {
  1022. // set chunk size (max: 100MB)
  1023. $chunkSizeBytes = 100 * 1024 * 1024;
  1024. $memory = $this->getIniBytes('memory_limit');
  1025. if ($memory > 0) {
  1026. $chunkSizeBytes = max(262144 , min([
  1027. $chunkSizeBytes,
  1028. (intval($memory / 4 / 256) * 256)
  1029. ]));
  1030. }
  1031. if ($fstat['size'] < $chunkSizeBytes) {
  1032. $isResource = false;
  1033. $contents = stream_get_contents($contents);
  1034. }
  1035. }
  1036.  
  1037. if (! $mime) {
  1038. $mime = Util::guessMimeType($fileName, $isResource ? '' : $contents);
  1039. }
  1040. $file->setMimeType($mime);
  1041.  
  1042. if ($isResource) {
  1043. $client = $this->service->getClient();
  1044. // Call the API with the media upload, defer so it doesn't immediately return.
  1045. $client->setDefer(true);
  1046. if ($mode === 'insert') {
  1047. $request = $this->service->files->create($file, [
  1048. 'fields' => self::FETCHFIELDS_GET
  1049. ]);
  1050. } else {
  1051. $request = $this->service->files->update($srcFile->getId(), $file, [
  1052. 'fields' => self::FETCHFIELDS_GET
  1053. ]);
  1054. }
  1055.  
  1056. // Create a media file upload to represent our upload process.
  1057. $media = new Google_Http_MediaFileUpload($client, $request, $mime, null, true, $chunkSizeBytes);
  1058. $media->setFileSize($fstat['size']);
  1059. // Upload the various chunks. $status will be false until the process is
  1060. // complete.
  1061. $status = false;
  1062. $handle = $contents;
  1063. while (! $status && ! feof($handle)) {
  1064. // read until you get $chunkSizeBytes from TESTFILE
  1065. // fread will never return more than 8192 bytes if the stream is read buffered and it does not represent a plain file
  1066. // An example of a read buffered file is when reading from a URL
  1067. $chunk = $this->readFileChunk($handle, $chunkSizeBytes);
  1068. $status = $media->nextChunk($chunk);
  1069. }
  1070. // The final value of $status will be the data from the API for the object
  1071. // that has been uploaded.
  1072. if ($status != false) {
  1073. $obj = $status;
  1074. }
  1075.  
  1076. $client->setDefer(false);
  1077. } else {
  1078. $params = [
  1079. 'data' => $contents,
  1080. 'uploadType' => 'media',
  1081. 'fields' => self::FETCHFIELDS_GET
  1082. ];
  1083. if ($mode === 'insert') {
  1084. $obj = $this->service->files->create($file, $params);
  1085. } else {
  1086. $obj = $this->service->files->update($srcFile->getId(), $file, $params);
  1087. }
  1088. }
  1089.  
  1090. if ($obj instanceof Google_Service_Drive_DriveFile) {
  1091. $this->cacheFileObjects[$obj->getId()] = $obj;
  1092. if ($mode === 'insert') {
  1093. $this->cacheFileObjects[$fileName] = $obj;
  1094. }
  1095. $result = $this->normaliseObject($obj, Util::dirname($path));
  1096.  
  1097. if ($visibility = $config->get('visibility')) {
  1098. if ($this->setVisibility($path, $visibility)) {
  1099. $result['visibility'] = $visibility;
  1100. }
  1101. }
  1102.  
  1103. return $result;
  1104. }
  1105.  
  1106. return false;
  1107. }
  1108.  
  1109. /**
  1110. * Read file chunk
  1111. *
  1112. * @param resource $handle
  1113. * @param int $chunkSize
  1114. *
  1115. * @return string
  1116. */
  1117. protected function readFileChunk($handle, $chunkSize)
  1118. {
  1119. $byteCount = 0;
  1120. $giantChunk = '';
  1121. while (! feof($handle)) {
  1122. // fread will never return more than 8192 bytes if the stream is read buffered and it does not represent a plain file
  1123. $chunk = fread($handle, 8192);
  1124. $byteCount += strlen($chunk);
  1125. $giantChunk .= $chunk;
  1126. if ($byteCount >= $chunkSize) {
  1127. return $giantChunk;
  1128. }
  1129. }
  1130. return $giantChunk;
  1131. }
  1132.  
  1133. /**
  1134. * Return bytes from php.ini value
  1135. *
  1136. * @param string $iniName
  1137. * @param string $val
  1138. * @return number
  1139. */
  1140. protected function getIniBytes($iniName = '', $val = '')
  1141. {
  1142. if ($iniName !== '') {
  1143. $val = ini_get($iniName);
  1144. if ($val === false) {
  1145. return 0;
  1146. }
  1147. }
  1148. $val = trim($val, "bB \t\n\r\0\x0B");
  1149. $last = strtolower($val[strlen($val) - 1]);
  1150. $val = (int)$val;
  1151. switch ($last) {
  1152. case 't':
  1153. $val *= 1024;
  1154. case 'g':
  1155. $val *= 1024;
  1156. case 'm':
  1157. $val *= 1024;
  1158. case 'k':
  1159. $val *= 1024;
  1160. }
  1161. return $val;
  1162. }
  1163. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement