You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

131 lines
3.6 KiB

  1. <?php
  2. /**
  3. * SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors
  4. * SPDX-License-Identifier: AGPL-3.0-or-later
  5. */
  6. namespace OC\Files\ObjectStore;
  7. use Aws\Result;
  8. use Exception;
  9. use OCP\Files\ObjectStore\IObjectStore;
  10. use OCP\Files\ObjectStore\IObjectStoreMultiPartUpload;
  11. class S3 implements IObjectStore, IObjectStoreMultiPartUpload, IObjectStoreMetaData {
  12. use S3ConnectionTrait;
  13. use S3ObjectTrait;
  14. public function __construct($parameters) {
  15. $parameters['primary_storage'] = true;
  16. $this->parseParams($parameters);
  17. }
  18. /**
  19. * @return string the container or bucket name where objects are stored
  20. * @since 7.0.0
  21. */
  22. public function getStorageId() {
  23. return $this->id;
  24. }
  25. public function initiateMultipartUpload(string $urn): string {
  26. $upload = $this->getConnection()->createMultipartUpload([
  27. 'Bucket' => $this->bucket,
  28. 'Key' => $urn,
  29. ] + $this->getSSECParameters());
  30. $uploadId = $upload->get('UploadId');
  31. if ($uploadId === null) {
  32. throw new Exception('No upload id returned');
  33. }
  34. return (string) $uploadId;
  35. }
  36. public function uploadMultipartPart(string $urn, string $uploadId, int $partId, $stream, $size): Result {
  37. return $this->getConnection()->uploadPart([
  38. 'Body' => $stream,
  39. 'Bucket' => $this->bucket,
  40. 'Key' => $urn,
  41. 'ContentLength' => $size,
  42. 'PartNumber' => $partId,
  43. 'UploadId' => $uploadId,
  44. ] + $this->getSSECParameters());
  45. }
  46. public function getMultipartUploads(string $urn, string $uploadId): array {
  47. $parts = [];
  48. $isTruncated = true;
  49. $partNumberMarker = 0;
  50. while ($isTruncated) {
  51. $result = $this->getConnection()->listParts([
  52. 'Bucket' => $this->bucket,
  53. 'Key' => $urn,
  54. 'UploadId' => $uploadId,
  55. 'MaxParts' => 1000,
  56. 'PartNumberMarker' => $partNumberMarker,
  57. ] + $this->getSSECParameters());
  58. $parts = array_merge($parts, $result->get('Parts') ?? []);
  59. $isTruncated = $result->get('IsTruncated');
  60. $partNumberMarker = $result->get('NextPartNumberMarker');
  61. }
  62. return $parts;
  63. }
  64. public function completeMultipartUpload(string $urn, string $uploadId, array $result): int {
  65. $this->getConnection()->completeMultipartUpload([
  66. 'Bucket' => $this->bucket,
  67. 'Key' => $urn,
  68. 'UploadId' => $uploadId,
  69. 'MultipartUpload' => ['Parts' => $result],
  70. ] + $this->getSSECParameters());
  71. $stat = $this->getConnection()->headObject([
  72. 'Bucket' => $this->bucket,
  73. 'Key' => $urn,
  74. ] + $this->getSSECParameters());
  75. return (int) $stat->get('ContentLength');
  76. }
  77. public function abortMultipartUpload($urn, $uploadId): void {
  78. $this->getConnection()->abortMultipartUpload([
  79. 'Bucket' => $this->bucket,
  80. 'Key' => $urn,
  81. 'UploadId' => $uploadId,
  82. ]);
  83. }
  84. public function getObjectMetaData(string $urn): array {
  85. $object = $this->getConnection()->headObject([
  86. 'Bucket' => $this->bucket,
  87. 'Key' => $urn
  88. ] + $this->getSSECParameters())->toArray();
  89. return [
  90. 'mtime' => $object['LastModified'],
  91. 'etag' => trim($object['ETag'], '"'),
  92. 'size' => (int) ($object['Size'] ?? $object['ContentLength']),
  93. ];
  94. }
  95. public function listObjects(string $prefix = ''): \Iterator {
  96. $results = $this->getConnection()->getPaginator('ListObjectsV2', [
  97. 'Bucket' => $this->bucket,
  98. 'Prefix' => $prefix,
  99. ] + $this->getSSECParameters());
  100. foreach ($results as $result) {
  101. if (is_array($result['Contents'])) {
  102. foreach ($result['Contents'] as $object) {
  103. yield [
  104. 'urn' => basename($object['Key']),
  105. 'metadata' => [
  106. 'mtime' => $object['LastModified'],
  107. 'etag' => trim($object['ETag'], '"'),
  108. 'size' => (int) ($object['Size'] ?? $object['ContentLength']),
  109. ],
  110. ];
  111. }
  112. }
  113. }
  114. }
  115. }