My Laravel application needs to manipulate files present in multiple buckets simultaneously into a single session. So, I couldn't find a way to change several times the current bucket, since my .env file is like this:
S3_KEY='MY-KEY'
S3_SECRET='MySeCret'
S3_REGION='us-east-1'
S3_BUCKET='my-first-used-bucket'
I found somewhere that I could do this:
Config::set('filesystems.disks.s3.bucket', 'another-bucket');
but It works only once. What I need is something like:
Storage::disk('s3')->put('/bucket-name/path/filename.jpg', $file, 'public');
Where /bucket-name/ could be any bucket that I already create. What can I do? Thanks a lot!
You are correct in that Config::set(); only works once per request. My estimation is that this is done intentionally to stop the kind of thing you are attempting to do in your code example.
In config/filesystems.php you can list any number of "disks". These are locations of your file repositories. It looks like so:
'disks' => [
'local' => [
'driver' => 'local',
'root' => storage_path('app'),
],
'ftp' => [
'driver' => 'ftp',
'host' => 'ftp.example.com',
'username' => 'your-username',
'password' => 'your-password',
// Optional FTP Settings...
// 'port' => 21,
// 'root' => '',
// 'passive' => true,
// 'ssl' => true,
// 'timeout' => 30,
],
's3' => [
'driver' => 's3',
'key' => env('S3_KEY',''),
'secret' => env('S3_SECRET',''),
'region' => env('S3_REGION',''),
'bucket' => env('S3_BUCKET',''),
],
]
The Solution
The solution is to create a new disk of the extra buckets you want to use. Treat your buckets like different disks.
Note: The user that the S3_Key belongs to needs to have permissions to perform your required actions on the S3 buckets you are setting up as additional 'disks'.
'disks' => [
//All your other 'disks'
...
//My default bucket details.
's3' => [
'driver' => 's3',
'key' => env('S3_KEY',''),
'secret' => env('S3_SECRET',''),
'region' => env('S3_REGION',''),
'bucket' => env('S3_BUCKET',''),
],
's3MyOtherBucketName' => [
'driver' => 's3',
'key' => env('S3_KEY',''),
'secret' => env('S3_SECRET',''),
'region' => env('S3_REGION',''),
'bucket' => 'myOtherBucketName',
],
's3YetAnotherBucketName' => [
'driver' => 's3',
'key' => env('S3_KEY',''),
'secret' => env('S3_SECRET',''),
'region' => env('S3_REGION',''),
'bucket' => 'yetAnotherBucketName',
],
]
Then whenever you want to access the bucket of your choice call it like so:
Storage::disk('s3')->put($fileName, $data);
Storage::disk('s3MyOtherBucketName')->put($anotherFileName, $moreData);
Storage::disk('s3YetAnotherBucketName')->put($yetAnotherFileName, $evenMoreData);
If you have dynamic buckets you also can create a new instance like this:
$storage = Storage::createS3Driver([
'driver' => 's3',
'key' => 'your-key',
'secret' => 'your-secret',
'region' => 'us-east-1',
'bucket' => $bucketName,
]);
$storage->put('path/to/file.png', $content);
You can add the buckets to the filesystems config like so:
'disks' => [
's3' => [
'bucket1' => [
'driver' => 's3',
'key' => env('AWS_BUCKET1_ACCESS_KEY_ID'),
'secret' => env('AWS_BUCKET1_SECRET_ACCESS_KEY'),
'region' => env('AWS_BUCKET1_DEFAULT_REGION'),
'bucket' => env('AWS_BUCKET1_BUCKET'),
'url' => env('AWS_BUCKET1_URL'),
],
'bucket2' => [
'driver' => 's3',
'key' => env('AWS_BUCKET2_ACCESS_KEY_ID'),
'secret' => env('AWS_BUCKET2_SECRET_ACCESS_KEY'),
'region' => env('AWS_BUCKET2_DEFAULT_REGION'),
'bucket' => env('AWS_BUCKET2_BUCKET'),
'url' => env('AWS_BUCKET2_URL'),
],
],
],
Then you can access the server using the following:
\Storage::disk('s3.bucket1')->put('path/to/file.png', $content);
I have created a class for myself and named it StorageProxy. You can find it here.
Let's say you want to work with multiple drivers, one of which is an S3-Compatible Object Storage but you may need to change the storage driver on-the-fly, local for example. No problem, the StorageProxy deals with it! (similar to mc - MinIO Client)
Usage:
StorageProxy::touch(?string $disk, ?string $bucket)->{everyStorageMethod}();
If no disk is specified, it uses the default disk (it's defined in filesystem.php).
Example:
For the local driver our file exists in images/sample.jpg
For s3 driver our file exists in images bucket with the name of sample.jpg
Our filesystem.php file should look like this:
'localhost' => [
'driver' => 'local',
'root' => storage_path('/'),
],
'DO' => [
'driver' => 's3',
'key' => env('AWS_ACCESS_KEY_ID'),
'secret' => env('AWS_SECRET_ACCESS_KEY'),
'region' => env('AWS_DEFAULT_REGION'),
// No need to specify the `bucket` property here
],
Since it's a proxy class, all Storage methods are available in StorageProxy too. Also, you don't need to remove the bucket name from the file path for s3 disks.
// For local disk
$disk = 'localhost';
StorageProxy::touch($disk)->exists("images/sample.jpg"); // true
// For S3 disk
$disk = 'DO';
StorageProxy::touch($disk)->exists("images/sample.jpg"); // true
StorageProxy::touch($disk, "images")->exists("images/sample.jpg"); // true
StorageProxy::touch($disk, "images")->bucketDetection(false)->exists("images/sample.jpg"); // false - the file path is considered as images/images/sample.jpg
StorageProxy::touch($disk, "images")->exists("sample.jpg"); // true
Related
This is my config/filesystems.php file.
'default' => env('FILESYSTEM_DRIVER', 's3'),
'disks' => [
's3' => [
'driver' => 's3',
'key' => env('AWS_ACCESS_KEY_ID'),
'secret' => env('AWS_SECRET_ACCESS_KEY'),
],
'b2' => [
'driver' => 's3',
'key' => env('B2_KEY_ID'),
'secret' => env('B2_APP_KEY'),
],
]
How can I get default filesystem's key value?
I think this is possible.
config("filesystems.disks." . config("filesystems.default") . ".key");
I believe there is any better way.
Can anyone help me?
If you want to change config value you should call config helper function and pass the value you want to set as the second argument. You can check it in Laravel documentation
I have set my laravel/voyager so that It will upload the user avatar to S3.
Uploading was successful but user avatar is broken. I got 403. What is wrong ?
config/voyager.php
storage' => [
'disk' => 's3',
],
config/filesystems.php
's3' => [
'driver' => 's3',
'key' => env('S3_ACCESS_KEY_ID'),
'secret' => env('S3_SECRET_ACCESS_KEY'),
'region' => env('REGION'),
'bucket' => env('BUCKET')
],
.env
S3_ACCESS_KEY_ID=accesskeyid
S3_SECRET_ACCESS_KEY=accesskey
BUCKET=bucket
REGION=region
You need to make sure that your S3 bucket is publicly accessible. Otherwise you will not be able to view the image without a token.
Basically what you need to do when you're trying to upload a new image, is to set the visibility to public, otherwise you get 503.
Storage::disk('s3')->setVisibility($image,'public');
And if the image or folder already exists, you need to change the visibility in the S3 console to public.
Or an alternative is to set the visibility in the config/filesystems.php file to public
's3' => [
'driver' => 's3',
'key' => env('AWS_ACCESS_KEY_ID'),
'secret' => env('AWS_SECRET_ACCESS_KEY'),
'region' => env('AWS_DEFAULT_REGION'),
'bucket' => env('AWS_BUCKET'),
'url' => env('AWS_URL'),
'endpoint' => env('AWS_ENDPOINT'),
'visibility' => 'public'
],
Hope this can help anybody.
I'm trying to load a external JSON file into my laravel application. Below you will find a snippet of my code.
$location = __DIR__.'/config.json';
echo $location; // Results in correct file location
echo File::exists($location); // Return 1
echo Storage::get($location); // Throws a FileNotFound Exception
How can the File::exists() method return true and the Storage::get() throw an exception?
My config/filesystems.php file:
<?php
return [
'default' => env('FILESYSTEM_DRIVER', 'local'),
'cloud' => env('FILESYSTEM_CLOUD', 's3'),
'disks' => [
'local' => [
'driver' => 'local',
'root' => storage_path('app'),
],
'public' => [
'driver' => 'local',
'root' => storage_path('app/public'),
'url' => env('APP_URL').'/storage',
'visibility' => 'public',
],
's3' => [
'driver' => 's3',
'key' => env('AWS_ACCESS_KEY_ID'),
'secret' => env('AWS_SECRET_ACCESS_KEY'),
'region' => env('AWS_DEFAULT_REGION'),
'bucket' => env('AWS_BUCKET'),
],
],
];
Why use the Storage class for this? That class is specifically for accessing the storage folder or external file servers. You stored your file right next to your controller/code, so it's probably easier to do it the old-fashioned way.
$location = __DIR__.'/config.json';
if (File::exists($location)) {
$string = file_get_contents($location);
$json = json_decode($string, true);
// logic
} else {
// Do something
}
You can try accessing it through the file facade by
File::get($location);
The error is probably because Storage is trying to access the file from its default path (./project/storage/app) plus what you send it. My best advice, is to add a new disk
like this
// config/filesystems.php
'customDrive' => [
'driver' => 'local',
'root' => base_path(), // here put your full path
],
and acess it through
Storage::disk("customDrive")->get("config.json");
Source https://laravel.com/docs/5.6/filesystem
I want to configure the storage path in a Laravel 5.1 using the .env file. My bootstrap/app.php looks like this:
<?php
$app = new \Illuminate\Foundation\Application(
realpath(__DIR__.'/../')
);
$app->useStoragePath(getenv('STORAGE_PATH'));
and the relevant line in .env file is:
STORAGE_PATH=/var/www/storage
This doesn't work. I figured out the Dotenv library is initialised after the bootstrap is processed so the .env variables are not available in bootstrap.php.
Is there a different place where I can set the storage path and the .env variables are available?
In config/filesystems.php you can set your storage path. Try setting your storage path there and see if it works. Note that the example below is my suggestion as to how your config/filesystems.php should look. Don't mind the s3 setup. That's a part of my project.
Remember to remove $app->useStoragePath(getenv('STORAGE_PATH')); from app.php
return [
'default' => 's3',
'cloud' => 's3',
'disks' => [
'local' => [
'driver' => 'local',
'root' => env('STORAGE_PATH'),
],
's3' => [
'driver' => 's3',
'key' => env('AWS_KEY'),
'secret' => env('AWS_SECRET'),
'region' => env('AWS_REGION'),
'bucket' => env('AWS_BUCKET'),
],
'rackspace' => [
'driver' => 'rackspace',
'username' => 'your-username',
'key' => 'your-key',
'container' => 'your-container',
'endpoint' => 'https://identity.api.rackspacecloud.com/v2.0/',
'region' => 'IAD',
],
],
];
I'm using aws-php-sdk with laravel.
But, i have a problem.
I want to use different key and secret for different amazon sevices.
For example:
Amazon S3:
Key: AAAAAA
Secret: BBBBB
Amazon EC2:
Key: DDDDD
Secret: EEEEE
is it possible? if this is possible, how can i do this?
Yes, you can do something like this.
<?php
return [
'default' => 'local',
'cloud' => 's3',
'disks' => [
's3_1' => [
'driver' => 's3',
'key' => 'AAAAAA',
'secret' => 'BBBBB',
'region' => 'us-east-1',
'bucket' => 'bucket1',
],
's3_2' => [
'driver' => 's3',
'key' => 'DDDDD',
'secret' => 'EEEEE',
'region' => 'us-east-1',
'bucket' => 'bucket2',
],
];
Then refer to them
$s3_1 = Storage::disk('s3_1');
$s3_2 = Storage::disk('s3_2');
I would extract them to a .env file.
.env
S3_KEY1=AAAAAA
S3_SECRET1=BBBBB
S3_REGION1=us-east-1
S3_BUCKET1=bucket1
S3_KEY2=DDDDD
S3_SECRET2=EEEEE
S3_REGION2=us-east-1
S3_BUCKET2=bucket1
config/filesystem.php
<?php
return [
'default' => 'local',
'cloud' => 's3',
'disks' => [
's3_1' => [
'driver' => 's3',
'key' => env('S3_KEY1'),
'secret' => env('S3_SECRET1'),
'region' => env('S3_REGION1'),
'bucket' => env('S3_BUCKET1'),
],
's3_2' => [
'driver' => 's3',
'key' => env('S3_KEY2'),
'secret' => env('S3_SECRET2'),
'region' => env('S3_REGION2'),
'bucket' => env('S3_BUCKET_MUSES2'),
],
];