diff options
author | Robin Appelman <robin@icewind.nl> | 2020-03-19 14:32:25 +0100 |
---|---|---|
committer | Robin Appelman <robin@icewind.nl> | 2020-04-01 15:21:05 +0200 |
commit | 7b07e7251c8a92e95da922f34dde158ddffbeeee (patch) | |
tree | 3cae032a5965aeb2604896c5aa51f263c33103fb /lib/private/Files/ObjectStore | |
parent | 14401efb0f6792415f15c92c1db07fe9e25ea466 (diff) | |
download | nextcloud-server-7b07e7251c8a92e95da922f34dde158ddffbeeee.tar.gz nextcloud-server-7b07e7251c8a92e95da922f34dde158ddffbeeee.zip |
make seekable s3 stream generic
Signed-off-by: Robin Appelman <robin@icewind.nl>
Diffstat (limited to 'lib/private/Files/ObjectStore')
-rw-r--r-- | lib/private/Files/ObjectStore/S3ObjectTrait.php | 36 | ||||
-rw-r--r-- | lib/private/Files/ObjectStore/S3SeekableReadStream.php | 139 |
2 files changed, 25 insertions, 150 deletions
diff --git a/lib/private/Files/ObjectStore/S3ObjectTrait.php b/lib/private/Files/ObjectStore/S3ObjectTrait.php index 1e9d095b9eb..0939dd2a23c 100644 --- a/lib/private/Files/ObjectStore/S3ObjectTrait.php +++ b/lib/private/Files/ObjectStore/S3ObjectTrait.php @@ -30,6 +30,7 @@ use Aws\S3\MultipartUploader; use Aws\S3\ObjectUploader; use Aws\S3\S3Client; use Icewind\Streams\CallbackWrapper; +use OC\Files\Stream\SeekableHttpStream; const S3_UPLOAD_PART_SIZE = 524288000; // 500MB @@ -49,16 +50,29 @@ trait S3ObjectTrait { * @since 7.0.0 */ function readObject($urn) { - $context = stream_context_create([ - 's3seek' => [ - 'client' => $this->getConnection(), - 'bucket' => $this->bucket, - 'urn' => $urn, - ], - ]); + return SeekableHttpStream::open(function ($range) use ($urn) { + $command = $this->getConnection()->getCommand('GetObject', [ + 'Bucket' => $this->bucket, + 'Key' => $urn, + 'Range' => 'bytes=' . $range, + ]); + $request = \Aws\serialize($command); + $headers = []; + foreach ($request->getHeaders() as $key => $values) { + foreach ($values as $value) { + $headers[] = "$key: $value"; + } + } + $opts = [ + 'http' => [ + 'protocol_version' => 1.1, + 'header' => $headers, + ], + ]; - S3SeekableReadStream::registerIfNeeded(); - return fopen('s3seek://', 'r', false, $context); + $context = stream_context_create($opts); + return fopen($request->getUri(), 'r', false, $context); + }); } /** @@ -76,7 +90,7 @@ trait S3ObjectTrait { $uploader = new MultipartUploader($this->getConnection(), $countStream, [ 'bucket' => $this->bucket, 'key' => $urn, - 'part_size' => S3_UPLOAD_PART_SIZE + 'part_size' => S3_UPLOAD_PART_SIZE, ]); try { @@ -103,7 +117,7 @@ trait S3ObjectTrait { function deleteObject($urn) { $this->getConnection()->deleteObject([ 'Bucket' => $this->bucket, - 'Key' => $urn + 'Key' => $urn, ]); } diff --git a/lib/private/Files/ObjectStore/S3SeekableReadStream.php b/lib/private/Files/ObjectStore/S3SeekableReadStream.php deleted file mode 100644 index 70855537034..00000000000 --- a/lib/private/Files/ObjectStore/S3SeekableReadStream.php +++ /dev/null @@ -1,139 +0,0 @@ -<?php -/** - * - * @copyright Copyright (c) 2020, Lukas Stabe (lukas@stabe.de) - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * - */ - -namespace OC\Files\ObjectStore; - -/** - * A stream wrapper that uses http range requests to provide a seekable - * stream of a file in S3 storage. - */ -class S3SeekableReadStream { - private static $registered = false; - - /** - * Registers the stream wrapper using the `s3seek://` url scheme - * $return void - */ - public static function registerIfNeeded() { - if (!self::$registered) { - stream_wrapper_register( - 's3seek', - 'OC\Files\ObjectStore\S3SeekableReadStream' - ); - self::$registered = true; - } - } - - private $client; - private $bucket; - private $urn; - - private $current; - private $offset = 0; - - private function reconnect($range) { - if ($this->current != null) { - fclose($this->current); - } - - $command = $this->client->getCommand('GetObject', [ - 'Bucket' => $this->bucket, - 'Key' => $this->urn, - 'Range' => 'bytes=' . $range, - ]); - $request = \Aws\serialize($command); - $headers = []; - foreach ($request->getHeaders() as $key => $values) { - foreach ($values as $value) { - $headers[] = "$key: $value"; - } - } - $opts = [ - 'http' => [ - 'protocol_version' => 1.1, - 'header' => $headers, - ], - ]; - - $context = stream_context_create($opts); - $this->current = fopen($request->getUri(), 'r', false, $context); - - if ($this->current === false) {return false;} - - $responseHead = stream_get_meta_data($this->current)['wrapper_data']; - $contentRange = array_values(array_filter($responseHead, function ($v) { - return preg_match('#^content-range:#i', $v) === 1; - }))[0]; - - $content = trim(explode(':', $contentRange)[1]); - $range = trim(explode(' ', $content)[1]); - $begin = explode('-', $range)[0]; - $this->offset = intval($begin); - - return true; - } - - function stream_open($path, $mode, $options, &$opened_path) { - $o = stream_context_get_options($this->context)['s3seek']; - $this->bucket = $o['bucket']; - $this->urn = $o['urn']; - $this->client = $o['client']; - - return $this->reconnect('0-'); - } - - function stream_read($count) { - $ret = fread($this->current, $count); - $this->offset += strlen($ret); - return $ret; - } - - function stream_seek($offset, $whence) { - switch ($whence) { - case SEEK_SET: - if ($offset === $this->offset) {return true;} - return $this->reconnect($offset . '-'); - case SEEK_CUR: - if ($offset === 0) {return true;} - return $this->reconnect(($this->offset + $offset) . '-'); - case SEEK_END: - return false; - } - return false; - } - - function stream_tell() { - return $this->offset; - } - - function stream_stat() { - return fstat($this->current); - } - - function stream_eof() { - return feof($this->current); - } - - function stream_close() { - fclose($this->current); - } -} |