Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Better S3 client creation #22

Merged
merged 6 commits into from
Nov 13, 2020
Merged

Better S3 client creation #22

merged 6 commits into from
Nov 13, 2020

Conversation

jszobody
Copy link
Member

Instead of creating the S3 client directly in the S3File class, we'll now wire it up in the service provider. This way consumers can re-wire it up if they need to.

Additionally, this alters the config file structure just a bit, with the aws section matching the array structure passed to a new S3Client instance. This way consumers can publish the config file and directly control how the S3 client is created.

@jszobody jszobody linked an issue Jun 24, 2020 that may be closed by this pull request
@MarceauKa
Copy link

This is awesome man! I used your idea to adapt S3File to my needs and it works like a charm!

An idea that I've implemented is to add a setSize method to skip the calculateFilesize request. My files are coming from spatie/laravel-medialibrary so I already have this information (and with 2000+ files) time is super important.

Here's my custom S3File without your PR, just pass an instance of this class to the ZipStream@add method.

<?php

namespace App\Services;

use Aws\S3\S3Client;
use Illuminate\Config\Repository;
use Psr\Http\Message\StreamInterface;
use STS\ZipStream\Models\File;

use function GuzzleHttp\Psr7\stream_for;

class StreamS3File extends File
{
    protected ?string $region = null;
    protected ?S3Client $client = null;
    protected int $size = 0;

    public function calculateFilesize(): int
    {
        if ($this->size) {
            return $this->size;
        }

        $this->size = (int)$this->getS3Client()->headObject([
            'Bucket' => $this->getBucket(),
            'Key' => $this->getKey()
        ])->get('ContentLength');

        return $this->size;
    }

    public function setSize(int $size): self
    {
        $this->size = $size;

        return $this;
    }

    public function setS3Client(S3Client $client)
    {
        $this->client = $client;

        return $this;
    }

    public function getS3Client(): S3Client
    {
        if (!$this->client) {
            /** @var Repository $config */
            $config = app()->make('config');

            $this->client = new S3Client([
                'credentials' => [
                    'key' => $config->get('filesystems.disks.s3.key'),
                    'secret' => $config->get('filesystems.disks.s3.secret')
                ],
                'version' => 'latest',
                'endpoint' => $config->get('filesystems.disks.s3.endpoint'),
                'use_path_style_endpoint' => false,
                'region' => $config->get('filesystems.disks.s3.region')
            ]);
        }

        return $this->client;
    }

    public function getBucket(): string
    {
        return parse_url($this->getSource(), PHP_URL_HOST);
    }

    public function getKey(): string
    {
        return ltrim(parse_url($this->getSource(), PHP_URL_PATH), "/");
    }

    protected function buildReadableStream(): StreamInterface
    {
        return $this->getS3Client()->getObject([
            'Bucket' => $this->getBucket(),
            'Key' => $this->getKey()
        ])->get('Body');
    }

    protected function buildWritableStream(): StreamInterface
    {
        $this->getS3Client()->registerStreamWrapper();

        return stream_for(fopen($this->getSource(), 'w'));
    }
}

@jszobody jszobody merged commit ba4c3f6 into master Nov 13, 2020
@jszobody jszobody deleted the better-s3-clients branch November 13, 2020 14:47
@jszobody
Copy link
Member Author

Good call. I just added a setFilesize method in the base File class, so it's available for all file models. I merged this PR and tagged v3. I believe you can now do everything with v3 out of the box now, no need for a custom class.

The following should work:

$zip->add(
    File::make("s3://bucket-name/path/to/object.pdf", "Something.pdf")->setFilesize(12345)
);

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Allow anonymous S3 clients
2 participants