Swift.php 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137
  1. <?php
  2. /**
  3. * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
  4. * SPDX-FileCopyrightText: 2016 ownCloud, Inc.
  5. * SPDX-License-Identifier: AGPL-3.0-only
  6. */
  7. namespace OC\Files\ObjectStore;
  8. use GuzzleHttp\Client;
  9. use GuzzleHttp\Exception\BadResponseException;
  10. use GuzzleHttp\Psr7\Utils;
  11. use Icewind\Streams\RetryWrapper;
  12. use OCP\Files\NotFoundException;
  13. use OCP\Files\ObjectStore\IObjectStore;
  14. use OCP\Files\StorageAuthException;
  15. use Psr\Log\LoggerInterface;
  16. const SWIFT_SEGMENT_SIZE = 1073741824; // 1GB
  17. class Swift implements IObjectStore {
  18. /**
  19. * @var array
  20. */
  21. private $params;
  22. /** @var SwiftFactory */
  23. private $swiftFactory;
  24. public function __construct($params, ?SwiftFactory $connectionFactory = null) {
  25. $this->swiftFactory = $connectionFactory ?: new SwiftFactory(
  26. \OC::$server->getMemCacheFactory()->createDistributed('swift::'),
  27. $params,
  28. \OC::$server->get(LoggerInterface::class)
  29. );
  30. $this->params = $params;
  31. }
  32. /**
  33. * @return \OpenStack\ObjectStore\v1\Models\Container
  34. * @throws StorageAuthException
  35. * @throws \OCP\Files\StorageNotAvailableException
  36. */
  37. private function getContainer() {
  38. return $this->swiftFactory->getContainer();
  39. }
  40. /**
  41. * @return string the container name where objects are stored
  42. */
  43. public function getStorageId() {
  44. if (isset($this->params['bucket'])) {
  45. return $this->params['bucket'];
  46. }
  47. return $this->params['container'];
  48. }
  49. public function writeObject($urn, $stream, ?string $mimetype = null) {
  50. $tmpFile = \OC::$server->getTempManager()->getTemporaryFile('swiftwrite');
  51. file_put_contents($tmpFile, $stream);
  52. $handle = fopen($tmpFile, 'rb');
  53. if (filesize($tmpFile) < SWIFT_SEGMENT_SIZE) {
  54. $this->getContainer()->createObject([
  55. 'name' => $urn,
  56. 'stream' => Utils::streamFor($handle),
  57. 'contentType' => $mimetype,
  58. ]);
  59. } else {
  60. $this->getContainer()->createLargeObject([
  61. 'name' => $urn,
  62. 'stream' => Utils::streamFor($handle),
  63. 'segmentSize' => SWIFT_SEGMENT_SIZE,
  64. 'contentType' => $mimetype,
  65. ]);
  66. }
  67. }
  68. /**
  69. * @param string $urn the unified resource name used to identify the object
  70. * @return resource stream with the read data
  71. * @throws \Exception from openstack or GuzzleHttp libs when something goes wrong
  72. * @throws NotFoundException if file does not exist
  73. */
  74. public function readObject($urn) {
  75. try {
  76. $publicUri = $this->getContainer()->getObject($urn)->getPublicUri();
  77. $tokenId = $this->swiftFactory->getCachedTokenId();
  78. $response = (new Client())->request('GET', $publicUri,
  79. [
  80. 'stream' => true,
  81. 'headers' => [
  82. 'X-Auth-Token' => $tokenId,
  83. 'Cache-Control' => 'no-cache',
  84. ],
  85. ]
  86. );
  87. } catch (BadResponseException $e) {
  88. if ($e->getResponse() && $e->getResponse()->getStatusCode() === 404) {
  89. throw new NotFoundException("object $urn not found in object store");
  90. } else {
  91. throw $e;
  92. }
  93. }
  94. return RetryWrapper::wrap($response->getBody()->detach());
  95. }
  96. /**
  97. * @param string $urn Unified Resource Name
  98. * @return void
  99. * @throws \Exception from openstack lib when something goes wrong
  100. */
  101. public function deleteObject($urn) {
  102. $this->getContainer()->getObject($urn)->delete();
  103. }
  104. /**
  105. * @return void
  106. * @throws \Exception from openstack lib when something goes wrong
  107. */
  108. public function deleteContainer() {
  109. $this->getContainer()->delete();
  110. }
  111. public function objectExists($urn) {
  112. return $this->getContainer()->objectExists($urn);
  113. }
  114. public function copyObject($from, $to) {
  115. $this->getContainer()->getObject($from)->copy([
  116. 'destination' => $this->getContainer()->name . '/' . $to
  117. ]);
  118. }
  119. }