aboutsummaryrefslogtreecommitdiffstats
path: root/lib/private/Files/ObjectStore/S3.php
diff options
context:
space:
mode:
Diffstat (limited to 'lib/private/Files/ObjectStore/S3.php')
-rw-r--r--lib/private/Files/ObjectStore/S3.php142
1 files changed, 142 insertions, 0 deletions
diff --git a/lib/private/Files/ObjectStore/S3.php b/lib/private/Files/ObjectStore/S3.php
new file mode 100644
index 00000000000..72e1751e23d
--- /dev/null
+++ b/lib/private/Files/ObjectStore/S3.php
@@ -0,0 +1,142 @@
+<?php
+
+/**
+ * SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+namespace OC\Files\ObjectStore;
+
+use Aws\Result;
+use Exception;
+use OCP\Files\ObjectStore\IObjectStore;
+use OCP\Files\ObjectStore\IObjectStoreMetaData;
+use OCP\Files\ObjectStore\IObjectStoreMultiPartUpload;
+
+class S3 implements IObjectStore, IObjectStoreMultiPartUpload, IObjectStoreMetaData {
+ use S3ConnectionTrait;
+ use S3ObjectTrait;
+
+ public function __construct(array $parameters) {
+ $parameters['primary_storage'] = true;
+ $this->parseParams($parameters);
+ }
+
+ /**
+ * @return string the container or bucket name where objects are stored
+ * @since 7.0.0
+ */
+ public function getStorageId() {
+ return $this->id;
+ }
+
+ public function initiateMultipartUpload(string $urn): string {
+ $upload = $this->getConnection()->createMultipartUpload([
+ 'Bucket' => $this->bucket,
+ 'Key' => $urn,
+ ] + $this->getSSECParameters());
+ $uploadId = $upload->get('UploadId');
+ if ($uploadId === null) {
+ throw new Exception('No upload id returned');
+ }
+ return (string)$uploadId;
+ }
+
+ public function uploadMultipartPart(string $urn, string $uploadId, int $partId, $stream, $size): Result {
+ return $this->getConnection()->uploadPart([
+ 'Body' => $stream,
+ 'Bucket' => $this->bucket,
+ 'Key' => $urn,
+ 'ContentLength' => $size,
+ 'PartNumber' => $partId,
+ 'UploadId' => $uploadId,
+ ] + $this->getSSECParameters());
+ }
+
+ public function getMultipartUploads(string $urn, string $uploadId): array {
+ $parts = [];
+ $isTruncated = true;
+ $partNumberMarker = 0;
+
+ while ($isTruncated) {
+ $result = $this->getConnection()->listParts([
+ 'Bucket' => $this->bucket,
+ 'Key' => $urn,
+ 'UploadId' => $uploadId,
+ 'MaxParts' => 1000,
+ 'PartNumberMarker' => $partNumberMarker,
+ ] + $this->getSSECParameters());
+ $parts = array_merge($parts, $result->get('Parts') ?? []);
+ $isTruncated = $result->get('IsTruncated');
+ $partNumberMarker = $result->get('NextPartNumberMarker');
+ }
+
+ return $parts;
+ }
+
+ public function completeMultipartUpload(string $urn, string $uploadId, array $result): int {
+ $this->getConnection()->completeMultipartUpload([
+ 'Bucket' => $this->bucket,
+ 'Key' => $urn,
+ 'UploadId' => $uploadId,
+ 'MultipartUpload' => ['Parts' => $result],
+ ] + $this->getSSECParameters());
+ $stat = $this->getConnection()->headObject([
+ 'Bucket' => $this->bucket,
+ 'Key' => $urn,
+ ] + $this->getSSECParameters());
+ return (int)$stat->get('ContentLength');
+ }
+
+ public function abortMultipartUpload($urn, $uploadId): void {
+ $this->getConnection()->abortMultipartUpload([
+ 'Bucket' => $this->bucket,
+ 'Key' => $urn,
+ 'UploadId' => $uploadId,
+ ]);
+ }
+
+ private function parseS3Metadata(array $metadata): array {
+ $result = [];
+ foreach ($metadata as $key => $value) {
+ if (str_starts_with($key, 'x-amz-meta-')) {
+ $result[substr($key, strlen('x-amz-meta-'))] = $value;
+ }
+ }
+ return $result;
+ }
+
+ public function getObjectMetaData(string $urn): array {
+ $object = $this->getConnection()->headObject([
+ 'Bucket' => $this->bucket,
+ 'Key' => $urn
+ ] + $this->getSSECParameters())->toArray();
+ return [
+ 'mtime' => $object['LastModified'],
+ 'etag' => trim($object['ETag'], '"'),
+ 'size' => (int)($object['Size'] ?? $object['ContentLength']),
+ ] + $this->parseS3Metadata($object['Metadata'] ?? []);
+ }
+
+ public function listObjects(string $prefix = ''): \Iterator {
+ $results = $this->getConnection()->getPaginator('ListObjectsV2', [
+ 'Bucket' => $this->bucket,
+ 'Prefix' => $prefix,
+ ] + $this->getSSECParameters());
+
+ foreach ($results as $result) {
+ if (is_array($result['Contents'])) {
+ foreach ($result['Contents'] as $object) {
+ yield [
+ 'urn' => basename($object['Key']),
+ 'metadata' => [
+ 'mtime' => $object['LastModified'],
+ 'etag' => trim($object['ETag'], '"'),
+ 'size' => (int)($object['Size'] ?? $object['ContentLength']),
+ ],
+ ];
+ }
+ }
+ }
+ }
+}