No such file, Storing file into the local storage is not working on production - laravel

I have a laravel application deployed on Elasticbeanstalk, I'm working on a feature where I need to get a zip file from s3 bucket, store it into the local storage in order to be able to use laravel-zip to remove a pdf file from that zip.
the code is working locally, but I'm receiving 'No Such file error' after testing on production:
// get the file from s3 and store it into local storage
$contents = Storage::disk('s3')->get($file_name);
$zip_local_name = 'my_file.zip';
Storage::disk('local')->put($zip_local_name, $contents);
// use laravel-zip to remove the unwanted pdf file from the result
$manager = new ZipManager();
$file_path = storage_path('app').'\\'.$zip_local_name; // register existing zips
$manager->addZip(Zip::open($file_path));
$zip = $manager->getZip(0);
$zip->delete($data["Iso_Bus"]["field_name"].'.pdf');
$zip->close();
I made sure that the file exists on s3, so I think my main problem is that the file is not stored in the local storage.
Any help is appreciated
Edit filesystems configrations:
'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' => '***',
'secret' => '***',
'region' => '***',
'bucket' => '****',
'url' => '****',
],
],

You're getting full path for the file wrongly, try this one instead:
$file_path = Storage::disk('local')->path($zip_local_name);
Note: It's better to check if the Storage::put was successful before continue:
// get the file from s3 and store it into local storage
$contents = Storage::disk('s3')->get($file_name);
$zip_local_name = 'my_file.zip';
if (Storage::disk('local')->put($zip_local_name, $contents)) {
// `Storage::put` returns `true` on success, `false` on failure.
// use laravel-zip to remove the unwanted pdf file from the result
$manager = new ZipManager();
$file_path = $file_path = Storage::disk('local')->path($zip_local_name);
$manager->addZip(Zip::open($file_path));
$zip = $manager->getZip(0);
$zip->delete($data["Iso_Bus"]["field_name"].'.pdf');
$zip->close();
}

Related

Laravel storage link doesnt link with public

Try to upload my files to storage, and link it with my public folder.
I used
php artisan storage:link
Then, I upload photos like below;
$filename = uniqid().".".$file->getClientOriginalExtension();
$filePath = 'uploads/announcement';
Storage::disk('public')->putFileAs($filePath, $file, $filename, ['visibility' => 'public']);
$announcement->photo = $filename ?? $announcement->filename;
But my /public/storage folder is empty, when I upload files.
Whats wrong with it?
My filesystem.php is below;
'public' => [
'driver' => 'local',
'root' => storage_path(),
'url' => env('APP_URL').'/storage',
'visibility' => 'public',
],
this is the way i do it (i n livewire but the storePubliclyAs Is not livewire)
if($this->image != null){
$imageName = time().'.'.$this->image->extension();
$company->image= $imageName;
// on upload l image dans le folder
$this->image->storePubliclyAs('companylogos',$imageName);
}
companylogos is the name of the folder in (storage/app)
i had same problem as you first ! you need to make sure your folder is linked , for that you need to see a little logo on the folder like a link logo meaning that the two folders are sync (linked) , the error is to create both files and try to link them , no you have to create only the storage/app/companylogos folder first and then use artisan to link php artisan storage:link
of course you need to have prepared your file in config filesystems, this is how i did it
'links' => [
public_path('storage') => storage_path('app/public'),
public_path('profilecv') => storage_path('app/profilecv'),
public_path('companylogos') => storage_path('app/companylogos'),
],
I think you have to change file system
filesystem.php is below;
'public' => [
'driver' => 'local',
'root' => storage_path('app/public'),
'url' => env('APP_URL').'/storage',
'visibility' => 'public',
],

Laravel Storage SFTP and uploaded files permissions

I'm using Storage:SFTP (league/flysystem-sftp) to upload some files to an external server. Everything goes fine with a small issue: the files are uploaded with a 0644 (-rw-r--r--) permission. I've tried to use 'public' option on the put method as the example from docs, like
Storage::disk('remote-sftp')->put($filename, $contents, 'public');
but if fails returning FALSE and doesn't uploads the file.
If I remove the 'public' parameter, everything goes well but with the wrong permissions for file.
Is there any way to set the uploaded file permissions to something like 0666?
Finally the solution was a combination of Alpy's answer and configuration.
Calling setVisibility() went without failure, but keep permissions in 0644. Digging into the FTP/SFTP driver found that the 'public' permission has a pattern that can be assigned in config using 'permPublic' key, so writting in config/filesystems.php the desired octal permission it worked as spected.
'disks' => [
'local' => [
'driver' => 'local',
'root' => storage_path('app'),
],
'public' => [
'driver' => 'local',
'root' => storage_path('app/public'),
'url' => env('APP_URL').'/storage',
'visibility' => 'public',
],
'remote-sftp' => [
'driver' => 'sftp',
'host' => '222.222.222.222',
'username' => 'myuser',
'password' => 'mypassword',
'visibility' => 'public',
'permPublic' => 0766, /// <- this one did the trick
// 'port' => 22,
'root' => '/home',
// 'timeout' => 30,
],
],
];
File permissions are based on two factors. Visibility and Permissions. You can set these two options in the driver config as such:
'remote' => [
'driver' => 'sftp',
'host' => 'hostname',
'root' => '/',
'username' => 'user',
'password' => env('SYSTEM_PASS'),
'visibility' => 'public', // defaults to 'private'
'permPublic' => 0775
]
The permissions are set based on the visibility. So if you set 'permPublic' and don't set 'visibility' nothing will change as, the setVisibility() function uses 'visibility' to get the permissions.
vendor/league/flysystem-sftp/src/SftpAdapter.php
public function setVisibility($path, $visibility)
{
$visibility = ucfirst($visibility);
// We're looking for either permPublic or permPrivate
if (! isset($this->{'perm'.$visibility})) {
throw new InvalidArgumentException('Unknown visibility: '.$visibility);
}
$connection = $this->getConnection();
return $connection->chmod($this->{'perm'.$visibility}, $path);
}
The public default is 0755.
The private default is 0700.
umask
If 'visibility' is not set, I believe the permissions are set based on the remote system user's umask. You are able to modify this on the remote system, if you so choose. set umask for user
Directories
One thing to note while working with permissions is that this will only affect created files. To set the permissions on created directories, use the 'directoryPerm' attribute in your config.
This defaults to 0744
Here is a more global and efficient solution. I needed to control permission on Files and also directories when saving a file under recursive directories.
League SftpAdapter is creating the directories recursively if not exist yet. But the main problem is that, it won't add the permPublic => 0755 for directories, but only files, hence www-data user end up to have no access to the file if it's inside of a newly created directory. The solution is to dive in the code to see what's happening:
'disks' => [
'remote-sftp' => [
'driver' => 'sftp',
'host' => '222.222.222.222',
'port' => 22,
'username' => 'user',
'password' => 'password',
'visibility' => 'public', // set to public to use permPublic, or private to use permPrivate
'permPublic' => 0755, // whatever you want the public permission is, avoid 0777
'root' => '/path/to/web/directory',
'timeout' => 30,
'directoryPerm' => 0755, // whatever you want
],
],
In League\Flysystem\Sftp\StfpAdapter, there is 2 important attributes to see clearly:
/**
* #var array
*/
protected $configurable = ['host', 'hostFingerprint', 'port', 'username', 'password', 'useAgent', 'agent', 'timeout', 'root', 'privateKey', 'passphrase', 'permPrivate', 'permPublic', 'directoryPerm', 'NetSftpConnection'];
/**
* #var int
*/
protected $directoryPerm = 0744;
The $configurable is all possible keys to configure filesystem sftp driver above. You can change directoryPerm from 0744 to 0755 in config file:
'directoryPerm' => 0755,
HOWEVER, because there is kind a like a Bug in StfpAdapter https://github.com/thephpleague/flysystem-sftp/issues/81 that won't use the $config parameter on createDir:
$filesystem = Storage::disk('remote-sftp');
$filesystem->getDriver()->getAdapter()->setDirectoryPerm(0755);
$filesystem->put('dir1/dir2/'.$filename, $contents);
Or set it with public in purpose:
$filesystem->put('dir1/dir2/'.$filename, $contents, 'public');
I found this while looking for a solution and I think I've found what works in Laravel 9 after digging through the flysystem code.
Adding the following settings to my config looks to have done the trick.
'visibility' => 'public',
'permissions' => [
'file' => [
'public' => 0664,
'private' => 0664,
],
'dir' => [
'public' => 0775,
'private' => 0775,
],
],
Please try this:
Storage::disk('remote-sftp')->put($filename, $contents)->setVisibility( $filename, 'public');
assuming the filename is also having the path..
Storage::disk('sftp')->download(...

upload image corrupted using put() on laravel

my code is
$avatarName = $user->id.'_avatar'.time().'.'.request('avatar')->getClientOriginalExtension();
$path = request('avatar')->storeAs('avatars',$avatarName);
Storage::disk('public')->put($avatarName,$path);
and I did on filesystem.php
'public' => [
'driver' => 'local',
'root' => public_path().'/avatars',
'url' => env('APP_URL').'/public',
'visibility' => 'public',
],
image is uploading. But image is corrupted. Original image file size is 1.19MB, after upload image size is 31 Bytes. What should I do?
First you can make sure its a file and a valid file.
Then you can use storeAs :
if($request->hasFile('avatar') && $request->file('avatar')->isValid()){
$avatarName = $user->id.'_avatar'.time().'.'.request('avatar')->getClientOriginalExtension();
// public as the 3rd argument is the disk name to store file in
$file->storeAs('your_path_here', $avatarName, 'public');
}

Laravel download not working

In my application I have the need to:
upload a file
store information in the db
store the file in a local or remote filesystem
listing all the db rows with a link to download the file
remove the file from the db and from the filesystem
I am trying to develop the 4th but the solutions found here and here don't work for me.
My filesystem.php is:
'local' => [
'driver' => 'local',
'root' => storage_path('app'),
],
'public' => [
'driver' => 'local',
'root' => storage_path('app/public'),
'visibility' => 'public',
],
'myftpsite' => [
'driver' => 'ftp',
'host' => 'myhost',
'username' => 'ftpuser,
'password' => 'ftppwd',
// Optional FTP Settings...
// 'port' => 21,
'root' => '/WRK/FILE/TEST',
// 'passive' => true,
// 'ssl' => true,
// 'timeout' => 30,
],
In the Controller I store the file with:
... validation here ...
$path = $request->uploadfile->storeAs('', $request->uploadfile->getClientOriginalName(), self::STORAGEDISK);
$file = new TESTFile;
... db code here ...
$file->save();
At this point I would like to retrive the variable to pass to the download methods (url or path of my file). I found 2 ways
Storage::url($pspfile->filename) *return* **/storage/** accept.png
Storage::disk(self::STORAGEDISK)->getDriver()->getAdapter()->applyPathPrefix($pspfile->filename) *return* C:\xampp\htdocs\myLaravel\ **storage** \app\accept.png
Any help or suggestion to do it in a better way will be very appreciated.
EDIT
For the moment I separete local/public from FTP.
The download is working if in the Controller I modify
$path = $request->uploadfile->storeAs('',
$request->uploadfile->getClientOriginalName()
,self::STORAGEDISK);
$file->fullpath = $path;
with
$file->fullpath = storage_path('app\\') . $path;
where 'app\' is the storage_path configured as root in filesystem.php
Moreover I can avoid to hardcode and use
$file->fullpath = Storage::disk(self::STORAGEDISK)
->getDriver()
->getAdapter()
->getPathPrefix() . $path;
In this way the download method can use
return response()->download($pspfile->fullpath);
I am still looking for a way to retrive a valid scr attribute for an img tag.
In addition I would like the same with remote stored files (maybe with local temp dir and file?)
I made something similar some time ago. Maybe this example code helps you.
class FileController extends Controller
{
// ... other functions ...
public function download(File $file)
{
if (Storage::disk('public')->exists($file->path)) {
return response()->download(public_path('storage/' . $file->path), $file->name);
} else {
return back();
}
}
public function upload()
{
$this->validate(request(), [
'file-upload' => 'required|file',
]);
$path = request()->file('file-upload')->store('uploads', 'public');
$file = new File;
$file->name = request()->file('file-upload')->getClientOriginalName();
$file->path = $path;
$file->save();
return back();
}
}

Storing files outside the Laravel 5 Root Folder

I am developing a laravel 5 project and storing image files using imagine. I would want to store my image files in a folder outside the project's root folder. I am stuck at the moment The external folder where image files are supposed to be stored, I want to make it accessible via a sub-domain something like http://cdn.example.com Looking towards your solutions.
The laravel documentation could give you a helping hand.
Otherwise you could go to config/filesystems.php and add your own custom storage path for both local and production:
return [
'default' => 'custom',
'cloud' => 's3',
'disks' => [
'local' => [
'driver' => 'local',
'root' => storage_path().'/app',
],
'custom' => [
'driver' => 'custom',
'root' => '../path/to/your/new/storage/folder',
],
's3' => [
'driver' => 's3',
'key' => 'your-key',
'secret' => 'your-secret',
'region' => 'your-region',
'bucket' => 'your-bucket',
],
'rackspace' => [
'driver' => 'rackspace',
'username' => 'your-username',
'key' => 'your-key',
'container' => 'your-container',
'endpoint' => 'https://identity.api.rackspacecloud.com/v2.0/',
'region' => 'IAD',
],
],
];
get ur path name from base_path(); function, then from the string add your desired folder location.
suppose ur
base_path() = '/home/user/user-folder/your-laravel-project-folder/'
So ur desired path should be like this
$path = '/home/user/user-folder/your-target-folder/'.$imageName;
make sure u have the writing and reading permission
You can move all or a part of storage folder in any folder of yours in your server. You must put a link from old to new folder.
ln -s new_fodler_path older_folder_path
You can make a new virtual host to serve the new folder path.

Resources