I am using the Storage provider to upload files to rackspace like so...
$logo = $request->file('logo');
$content = fopen($logo->getRealPath(), 'r+');
\Storage::disk('cdn')->put('logo.png', $content);
Now, how can I get the url of the file above? I has been looking for a method in the API and it seems so impossible.
http://laravel.com/api/5.0/Illuminate/Filesystem/FilesystemAdapter.html
http://laravel.com/api/5.0/Illuminate/Filesystem/Filesystem.html
I usually store the disk public URL in the config/filesystems.php file. For example:
'google' => [
'driver' => 's3',
'key' => env('STORAGE_GOOGLE_KEY'),
'secret' => env('STORAGE_GOOGLE_SECRET'),
'bucket' => env('STORAGE_GOOGLE_BUCKET'),
'base_url' => 'https://storage.googleapis.com',
'public_url' => env('STORAGE_GOOGLE_PUBLIC_URL'),
],
Then in my model, I define a mutator for the field containing the file name:
public function getAvatarAttribute($value)
{
// If avatar isn't a URL, generate it
if (filter_var($value, FILTER_VALIDATE_URL) !== FALSE) {
return $value;
} else {
$disk = config('filesystems.default');
return config('filesystems.disks.'.$disk.'.public_url').'/avatars/'.$value;
}
}
This allows me:
To change the default disk at any time and immediately point all the references to my new disk.
To store actual URLs in the database if required. In my case, avatars may come from OAuth or actual file uploads, so I need to cater for both.
The issue here is the way the FlySystem adapter works.
For most operations it will just return a boolean indicating if an operation was successful or not.
Even Laravel's FlySystem wrapper also doesn't keep track of paths, so a workaround would be to build the path yourself after a successful upload.
By using the filesystem configuration, we can come up with something like this:
$cdn_url = config('filesystems.disks.cdn.container').'/logo.png';
You get the picture.
In recent Laravel releases you can customise your storage URL
https://laravel.com/docs/8.x/filesystem#url-host-customization
URL Host Customization
If you would like to pre-define the host for file URLs generated using
the Storage facade, you may add a url option to the disk's
configuration array:
'public' => [
'driver' => 'local',
'root' => storage_path('app/public'),
'url' => env('APP_URL').'/storage',
'visibility' => 'public',
],
example 'url' => 'https://cdn.example.com', or just add it to .env file ( by default for s3 it is AWS_URL)
Related
What I am trying to achieve is to remove the /storage from the URL, so that in the end it is www.example.com/images/x.jpg and not the default www.example.com/storage/x.jpg.
I have tried removing /storage from the url in config/filesystems.php like this:
// Original
'public' => [
'driver' => 'local',
'root' => storage_path('app/public'),
'url' => env('APP_URL') . '/storage',
'visibility' => 'public',
],
// Modified
'public' => [
'driver' => 'local',
'root' => storage_path('app/public'),
'url' => env('APP_URL'), // <-- changed
'visibility' => 'public',
],
but it is not working. I think the issue is that without a prefix it will be regarded as file inside a public folder.
Is it possible to achieve what i am trying to achieve?
The most straightforward way to accomplish this is to add a new disk. This way you can apply the new pattern to your images without affecting existing files and urls, along with a number of other benefits.
Step 1
Add your disk to config/filesystems.php:
'images' => [
'driver' => 'local',
'root' => storage_path('app/public/images'),
'url' => env('APP_URL') . '/images',
'visibility' => 'public',
],
Here is an example of how to save file uploads to your new disk from a controller:
// storeAs: path, filename, disk
$request->file('image')->storeAs('/', 'x.jpg', 'images')
And this is how you generate links to the image that look like http://example.com/images/x.jpg:
Storage::disk('images')->url('x.jpg')
Step 2
Here are three different options for serving files from the new path (you only need to pick one):
Option 1
Create a symlink in your public directory.
ln -s /var/www/example.com/storage/app/public/images /var/www/example.com/public/images
This is the same method Laravel uses for the default public disk (the /storage URLs).
As of Laravel 7, you can modify config/filesystems.php to manage additional symlinks:
/*
|--------------------------------------------------------------------------
| Symbolic Links
|--------------------------------------------------------------------------
|
| Here you may configure the symbolic links that will be created when the
| `storage:link` Artisan command is executed. The array keys should be
| the locations of the links and the values should be their targets.
|
*/
'links' => [
public_path('storage') => storage_path('app/public'),
public_path('images') => storage_path('app/public/images'),
],
Option 2
Create a route in your Laravel application to serve images:
Route::get('images/{file}', function ($file) {
return Storage::disk('images')->response($file);
// or to trigger downloads:
// return Storage::disk('images')->download($file);
});
The disadvantage to this option is that it serves each image using a PHP process, instead of being handled by the webserver like options 1 & 3.
Option 3
Create a rewrite rule in your webserver.
In nginx, it might look like this:
location /images/ {
root /var/www/example.com/storage/app/public/;
}
In Apache, you might use an alias:
Alias "/images" "/var/www/example.com/storage/app/public/images"
How do I tell the Laravel filesystem layer to use the s3 metadata on an EC2 instance? I don't want to provide hardcoded keys and secrets for my s3 buckets. I'm unclear on what the configuration should look like. When I exclude the key and secret from the filesystem configuration I get the following error
ErrorException
Undefined index: key
The fix is to leave empty placeholder values in place for key and secret. eg, in config/filesystems.php
return [
'cloud' => 's3',
'disks' => [
's3' => [
'driver' => 's3',
'key' => '',
'secret' => '',
'region' => env('S3_REGION'),
'bucket' => env('S3_BUCKET'),
],
],
];
The correct way to provide your credentials is by using the .env file.
In your .env file, add something like that:
EC2_SECRET=your_ec2_secret
EC3_KEY=your_ec2_key
and in the `` config file, use something like that:
'ec2' => [
...
'key' => env('EC2_SECRET'),
'secret' => env('EC3_KEY'),
],
You should now be able to use the service without having the credentials stored in the repository.
I have created a designated location to store all the uploaded images in public dir like this:
and I have the default config/filesystem.php for public driver like this:
'public' => [
'driver' => 'local',
'root' => storage_path('app/public'),
'url' => env('APP_URL').'/storage',
'visibility' => 'public',
],
Within my repository, I am trying to save the uploaded image like this:
// ... snipped
if (array_key_exists('logo', $validated)) {
$logoPath = $validated['logo']->store(
'uploads/logos',
'public'
);
$company->logo = url($logoPath);
}
$company->save();
In the database, I can see the value of logo field for the company record like this: http://my-app.local/uploads/logos/3hlsAhnnIPjd4zhdpfhvKw4tqDkpcCz23NczwhVM.png
However, the public/uploads/logos dir is empty. Any idea what might be wrong here? Am I supposed to use the ->move() method on the UploadedFile instead of ->store()?
You can use an alternative method for uploading images in your public directory.
$request->validate([
'logo' => 'image|required',
]);
if($request->hasFile('logo')){
$company->logo = $request->image->move('uploads/logo', $request->logo->hashName());
}
$company->save()
Actually, yes, it was your mistake, but not the one you just found. By default laravel local storage points to storage/app/public directory, because laravel wants you to act professionally and store all uploaded files in storage, not in some kind of public directory. But then you would ask - how on earth user should access non-public storage directly? And there is a caught - php artisan storage:link command. It creates symbolic link from public directory to storage directory (storage/app/public -> public/storage). This is mostly because of deployments and version management.
The server which I'm hosting my website does not support links so I cannot run the php artisan storage:link to link my storage directory into the public directory. I tried to remake the disk configuration in the filesystems.php to directly reference the public folder but it didn't seems to work either. Is there a way to upload a file using Laravel libraries directly into the public folder or will I have to use a php method?
You can create a new storage disc in config/filesystems.php:
'public_uploads' => [
'driver' => 'local',
'root' => public_path() . '/uploads',
],
And store files like this:
if(!Storage::disk('public_uploads')->put($path, $file_content)) {
return false;
}
You can pass disk to method of \Illuminate\Http\UploadedFile class:
$file = request()->file('uploadFile');
$file->store('toPath', ['disk' => 'public']);
or you can create new Filesystem disk and you can save it to that disk.
You can create a new storage disk in config/filesystems.php:
'my_files' => [
'driver' => 'local',
'root' => public_path() . '/myfiles',
],
in controller:
$file = request()->file('uploadFile');
$file->store('toPath', ['disk' => 'my_files']);
You should try this hopping you have added method="post" enctype="multipart/form-data" to your form. Note that the public path (uploadedimages) will be moved to will be your public folder in your project directory and that's where the uploaded images will be.
public function store (Request $request) {
$imageName = time().'.'.$request->image->getClientOriginalExtension();
$request->image->move(public_path('/uploadedimages'), $imageName);
// then you can save $imageName to the database
}
inside config/filesystem.php, add this :
'public_uploads' => [
'driver' => 'local',
'root' => public_path(),
],
and in the controller
$file = $request->file("contract");
$ext = $file->extension();
$filename = $file->storeAs('/contracts/', $contract->title.'.' . $ext,['disk' => 'public_uploads']);
I figured out the fix to this issue , in the config/filesystem.php add this line 'default' => 'public', in my case that fixed for me.
The most flexible is to edit .env file:
FILESYSTEM_DRIVER=public
In addition to every answer there, I would suggest to add another protection layer because it's a public folder.
For every file that you will have in your public folder that requires protection, do a route that will verify access to them, like
/files/images/cat.jpg
and route that looks like /files/images/{image_name} so you could verify the user against given file.
After a correct validation you just do
return response($full_server_filepath, 200)->header('Content-Type', 'image/jpeg');
It makes a work little harder, but much safer
The recommended way is to symlink the storage from the public directory.
To make these files accessible from the web, you should create a
symbolic link from public/storage to storage/app/public. Utilizing
this folder convention will keep your publicly accessible files in one
directory that can be easily shared across deployments when using zero
down-time deployment systems like Envoyer.
To create the symbolic link, you may use the storage:link Artisan
command:
Step 1: change setting in Filesystems.php
'disks' => [
'local' => [
'driver' => 'local',
'root' => storage_path('app'),
],
'public' => [
'driver' => 'local',
'root' => public_path('app/public'),
'url' => env('APP_URL').'/storage',
'visibility' => 'public',
],
Step 2: in Controller: Store file like this:
'indoc_file' => $request->indoc_file->store('app/file'),
Step 3: Go to file .ENV and change the Config like this:
FILESYSTEM_DRIVER=public
After that your file Location will be saved in Project like this:
Project\public\app\public\app\file
if you want to download file able do this on your view page:
<i class="fa fa-download"></i>
as alternative,
let laravel set the file stored in /storage/app folder.
if we want to read the file, just use like $publicPath = '../storage/app/'.$path;
In my Laravel API call, I use Storage to upload a file. Then I wold like to rename and move the file to a public directory by using:
$dest = '/var/www/public/uploads/';
Storage::disk('local')->put($filename.'.'.$extension, File::get($file));
$oldfilename = $filename.'.'.$extension;
Storage::move($oldfilename, $dest.$newImageName);
But instead of moving the file to the $dest directory, I get a folder created with the $dest dir name inside my storage/app folder and the file is copied there. I have also tried using double quotes around $dest.$newImageName but no success.
If you have an array ['disks']['public'] in config/filesystems.php you can do:
Storage::disk('public')->move($oldfilename, $dest.$newImageName);
If you don't create one that looks like this:
'public' => [
'driver' => 'local',
'root' => storage_path( 'app/public' ),
'visibility' => 'public',
],
And then you'll bi able to use the public "disk" with the storage facade.
Note that you don't have to specify the absolute path anymore, Laravel will know that the storage path is app/public.
EDIT
Another config option that might work:
'public' => [
'driver' => 'local',
'root' => public_path(),
'visibility' => 'public',
],