Swift.php 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164
  1. <?php
  2. /**
  3. * @copyright Copyright (c) 2016, ownCloud, Inc.
  4. *
  5. * @author Adrian Brzezinski <adrian.brzezinski@eo.pl>
  6. * @author Jörn Friedrich Dreyer <jfd@butonic.de>
  7. * @author Morris Jobke <hey@morrisjobke.de>
  8. * @author Robin Appelman <robin@icewind.nl>
  9. * @author Roeland Jago Douma <roeland@famdouma.nl>
  10. *
  11. * @license AGPL-3.0
  12. *
  13. * This code is free software: you can redistribute it and/or modify
  14. * it under the terms of the GNU Affero General Public License, version 3,
  15. * as published by the Free Software Foundation.
  16. *
  17. * This program is distributed in the hope that it will be useful,
  18. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  19. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  20. * GNU Affero General Public License for more details.
  21. *
  22. * You should have received a copy of the GNU Affero General Public License, version 3,
  23. * along with this program. If not, see <http://www.gnu.org/licenses/>
  24. *
  25. */
  26. namespace OC\Files\ObjectStore;
  27. use GuzzleHttp\Client;
  28. use GuzzleHttp\Exception\BadResponseException;
  29. use GuzzleHttp\Psr7\Utils;
  30. use Icewind\Streams\RetryWrapper;
  31. use OCP\Files\FileInfo;
  32. use OCP\Files\NotFoundException;
  33. use OCP\Files\ObjectStore\IObjectStore;
  34. use OCP\Files\StorageAuthException;
  35. use Psr\Log\LoggerInterface;
  36. const SWIFT_SEGMENT_SIZE = 1073741824; // 1GB
  37. class Swift implements IObjectStore {
  38. /**
  39. * @var array
  40. */
  41. private $params;
  42. /** @var SwiftFactory */
  43. private $swiftFactory;
  44. public function __construct($params, SwiftFactory $connectionFactory = null) {
  45. $this->swiftFactory = $connectionFactory ?: new SwiftFactory(
  46. \OC::$server->getMemCacheFactory()->createDistributed('swift::'),
  47. $params,
  48. \OC::$server->get(LoggerInterface::class)
  49. );
  50. $this->params = $params;
  51. }
  52. /**
  53. * @return \OpenStack\ObjectStore\v1\Models\Container
  54. * @throws StorageAuthException
  55. * @throws \OCP\Files\StorageNotAvailableException
  56. */
  57. private function getContainer() {
  58. return $this->swiftFactory->getContainer();
  59. }
  60. /**
  61. * @return string the container name where objects are stored
  62. */
  63. public function getStorageId() {
  64. if (isset($this->params['bucket'])) {
  65. return $this->params['bucket'];
  66. }
  67. return $this->params['container'];
  68. }
  69. public function writeObject($urn, $stream, string $mimetype = null) {
  70. $tmpFile = \OC::$server->getTempManager()->getTemporaryFile('swiftwrite');
  71. file_put_contents($tmpFile, $stream);
  72. $handle = fopen($tmpFile, 'rb');
  73. if (filesize($tmpFile) < SWIFT_SEGMENT_SIZE) {
  74. $this->getContainer()->createObject([
  75. 'name' => $urn,
  76. 'stream' => Utils::streamFor($handle),
  77. 'contentType' => $mimetype,
  78. ]);
  79. } else {
  80. $this->getContainer()->createLargeObject([
  81. 'name' => $urn,
  82. 'stream' => Utils::streamFor($handle),
  83. 'segmentSize' => SWIFT_SEGMENT_SIZE,
  84. 'contentType' => $mimetype,
  85. ]);
  86. }
  87. }
  88. /**
  89. * @param string $urn the unified resource name used to identify the object
  90. * @return resource stream with the read data
  91. * @throws \Exception from openstack or GuzzleHttp libs when something goes wrong
  92. * @throws NotFoundException if file does not exist
  93. */
  94. public function readObject($urn) {
  95. try {
  96. $publicUri = $this->getContainer()->getObject($urn)->getPublicUri();
  97. $tokenId = $this->swiftFactory->getCachedTokenId();
  98. $response = (new Client())->request('GET', $publicUri,
  99. [
  100. 'stream' => true,
  101. 'headers' => [
  102. 'X-Auth-Token' => $tokenId,
  103. 'Cache-Control' => 'no-cache',
  104. ],
  105. ]
  106. );
  107. } catch (BadResponseException $e) {
  108. if ($e->getResponse() && $e->getResponse()->getStatusCode() === 404) {
  109. throw new NotFoundException("object $urn not found in object store");
  110. } else {
  111. throw $e;
  112. }
  113. }
  114. return RetryWrapper::wrap($response->getBody()->detach());
  115. }
  116. /**
  117. * @param string $urn Unified Resource Name
  118. * @return void
  119. * @throws \Exception from openstack lib when something goes wrong
  120. */
  121. public function deleteObject($urn) {
  122. $this->getContainer()->getObject($urn)->delete();
  123. }
  124. /**
  125. * @return void
  126. * @throws \Exception from openstack lib when something goes wrong
  127. */
  128. public function deleteContainer() {
  129. $this->getContainer()->delete();
  130. }
  131. public function objectExists($urn) {
  132. return $this->getContainer()->objectExists($urn);
  133. }
  134. public function copyObject($from, $to) {
  135. $this->getContainer()->getObject($from)->copy([
  136. 'destination' => $this->getContainer()->name . '/' . $to
  137. ]);
  138. }
  139. public function bytesUsed(): int {
  140. return $this->getContainer()->bytesUsed;
  141. }
  142. public function bytesQuota(): int {
  143. return $this->getContainer()->getMetadata()['Quota-Bytes'] ?? FileInfo::SPACE_UNLIMITED;
  144. }
  145. }