CakePHP - need URL to route to the tmp directory? - cakephp-2.1

I am trying to create thumbnail pics using GD lib in Cake PHP.
I can write the resized thumbnail to the tmp directory, and create the sensible URL to show the image from the tmp directory:
//set up empty GD resource
$tmp = imagecreatetruecolor(200*$iw/$ih, 200);
//create thumbnail image in the empty GD resource
imagecopyresampled($tmp, $src, 0, 0, 0, 0,200*$iw/$ih, 200, $iw, $ih);
//build full path to thumbnail in the cakephp tmp directory
$thumbfile = APP.'tmp/thumb.jpg';
//build URL path to the same thumbnail
$thumblink = $this->webroot.'tmp/thumb.jpg';
//write jpeg to the tmp directory
$return=imagejpeg($tmp,$thumbfile);
//write out the image to client browser
echo "<img=\"$thumblink\" alt=\"thumb\" height=\"200\" width=\"200*$iw/$ih\">";
The thumbnail gets created and written to the tmp directory, but when I try to access the URL I get the following error message:
Error: TmpController could not be found.
Error: Create the class TmpController below in file: app/Controller/TmpController.php
Obviously I have a routing error - Cake tries to call the tmp controller, in stead of looking in the tmp direcectory. How can I fix this, or is there an alternative way to serve temporary thumbnails using GD lib?
I am planning to create unique thumbnails per session or user, but I need to get this working first.
Routing in Config/routes.php:
Router::connect('/', array('controller' => 'MsCake', 'action' => 'index'));
Router::connect('/pages/*', array('controller' => 'pages'));
CakePlugin::routes();
I looked at ThumbnailHelper, but that doesn't use GD Lib. I also need to be able to access files stored on non-apache accessible directories from outside, but I can't even access any temporary symbolic links to get to them. eg.
create a temporary symbolic link in the tmp directory, pointing to the file in question.
create a HTML link, pointing to the symbolic link using $this->webroot.'tmp/link-to-myfile', as above
...and I get the same error as above - Error: TmpController could not be found.

Don't do that
If you do anything to make files in the tmp dir web-accessible - you're severely lowering your site's security. Things in the tmp directory are never supposed to be web accessible.
Put your images in the webroot
A better idea is to put your temporary images in the webroot directory - which is the only directory that is ordinarily web accessible. For example:
$filename = md5($userId);
$thumbfile = APP.'webroot/img/cache/' . $filename . '.jpg';
...
$url = '/img/cache/' . $filename . '.jpg';
Or route to a controller action
Alternatively, route to a controller action to handle the request using the media view class. Note however though, that serving images with php is not free - there can be a noticable delay while your request is processed - where'as pointing at a static file does not have this cost/risk since it's just the webserver taking care of serving the content.

Since it's temporary, what you could do is to display the image as a data url instead of to the tmp directory, like so (replace from after imagecopyresampled() call):
ob_start();
imagepng($tmp);
$contents = ob_get_contents();
ob_end_clean();
imagedestroy($tmp);
//write out the image to client browser
echo "<img src='data:image/png;base64,".base64_encode($contents)."' alt='thumb' height='200' width='".(200*$iw/$ih)."'>";
This uses a bit more bandwidth since the image is base64 encoded rather than sent as binary.

Related

Adding Email Attachments From Local Storage

I am using laravel/lumen. I am able to save files using Storage::disk("local")->put(); in my storage directory. However now I want to attach a few of those files to an email and send, this is done with a job the error I get is
lumen.ERROR: exception 'Swift_IoException' with message 'Unable to
open file for reading
Now I read something about symbolic linking which I tried but that simply did not change the result, I was still unable to attach files i n my storage folder to my emails.
This is my directory structure:
/home/xxxxxx/:
-example.app
--storage
---app
----public
-public_html
--example.app
---storage
Attaching the file like this:
foreach ($params["attachments"] as $attachment) {
$mail->attach($attachment["file"], [
'as' => $attachment["name"],
'mime' => $attachment["mime"]
]);
}
You've already realized that attach() method expects the full path to the file. However, others may find useful know how to achieve that. So, in case you're using the default settings for local store, you may be able to get the full path by calling the storage_path() helper. This way:
/*
* This will return the full path to the file:
*
* /path/to/laravel/storage/app/attachment/path
*/
storage_path('app/' . $attachment['file']);
Okay so apparently swift mailer fails when you pass a full url instead of the real path to the file. Didn't know this, don't know if it's a bug or it was done by design.

Is it ok to change public storage folder name from 'public/storage' to 'public/public'?

Laravel recommends here to create symlink from public/storage to storage/app/public. I'm looking for some inputs on whether renaming public/storage to public/public would cause any issues in other configurations or packages.
I am doing this because all my filenames in db are relative to storage/app folder. I store it that way since I can then uniformly retrieve any file using storage_path() helper. For example:
Protected file: storage/app/internal/secret.doc => filename in db = internal/secret.doc
Public file: storage/app/public/common.doc => filename in db = public/common.doc
Then to retrieve them:
// get filename 'internal/secret.doc' or 'public/common.doc'
$filename = DB::table('files')->find(1)->value('name'); // find file where id=1 and get only the name field
$file = storage_path('app/'.$filename);
This way I don't have to wonder whether I need to add public/ folder while retrieving a specific file. Also when I use asset() helper to generate urls to public files, again I can simply say:
$url = asset($filename)`;
Whereas, if I continue to use public/storage as the symlinked folder then I would have to do:
$temp_filename = str_replace('public/', '', $filename);
$url = asset('storage/'.$temp_filename);
Let me know if you have a different take or spot some issues with the renaming.
Once public/storage is sym linked to storage/app/public, you don't want your filepath in asset() to reference public. Public folder is already the root of your webserver, and asset() assumes public directory base. So your asset would be in asset("storage/$filename"). That actually pulls from storage/app/public/$filename.

Laravel 5: How do you copy a local file to Amazon S3?

I'm writing code in Laravel 5 to periodically backup a MySQL database. My code thus far looks like this:
$filename = 'database_backup_'.date('G_a_m_d_y').'.sql';
$destination = storage_path() . '/backups/';
$database = \Config::get('database.connections.mysql.database');
$username = \Config::get('database.connections.mysql.username');
$password = \Config::get('database.connections.mysql.password');
$sql = "mysqldump $database --password=$password --user=$username --single-transaction >$destination" . $filename;
$result = exec($sql, $output); // TODO: check $result
// Copy database dump to S3
$disk = \Storage::disk('s3');
// ????????????????????????????????
// What goes here?
// ????????????????????????????????
I've seen solutions online that would suggest I do something like:
$disk->put('my/bucket/' . $filename, file_get_contents($destination . $filename));
However, for large files, isn't it wasteful to use file_get_contents()? Are there any better solutions?
There is a way to copy files without needing to load the file contents into memory using MountManager.
You will also need to import the following:
use League\Flysystem\MountManager;
Now you can copy the file like so:
$mountManager = new MountManager([
's3' => \Storage::disk('s3')->getDriver(),
'local' => \Storage::disk('local')->getDriver(),
]);
$mountManager->copy('s3://path/to/file.txt', 'local://path/to/output/file.txt');
You can always use a file resource to stream the file (advisable for large files) by doing something like this:
Storage::disk('s3')->put('my/bucket/' . $filename, fopen('path/to/local/file', 'r+'));
An alternative suggestion is proposed here. It uses Laravel's Storage facade to read the stream. The basic idea is something like this:
$inputStream = Storage::disk('local')->getDriver()->readStream('/path/to/file');
$destination = Storage::disk('s3')->getDriver()->getAdapter()->getPathPrefix().'/my/bucket/';
Storage::disk('s3')->getDriver()->putStream($destination, $inputStream);
You can try this code
$contents = Storage::get($file);
Storage::disk('s3')->put($newfile,$contents);
As Laravel document this is the easy way I found to copy data between two disks
Laravel has now putFile and putFileAs method to allow stream of file.
Automatic Streaming
If you would like Laravel to automatically manage
streaming a given file to your storage location, you may use the
putFile or putFileAs method. This method accepts either a
Illuminate\Http\File or Illuminate\Http\UploadedFile instance and will
automatically stream the file to your desired location:
use Illuminate\Http\File;
use Illuminate\Support\Facades\Storage;
// Automatically generate a unique ID for file name...
Storage::putFile('photos', new File('/path/to/photo'));
// Manually specify a file name...
Storage::putFileAs('photos', new File('/path/to/photo'), 'photo.jpg');
Link to doc: https://laravel.com/docs/5.8/filesystem (Automatic Streaming)
Hope it helps
Looking at the documentation the only way is using method put which needs file content. There is no method to copy file between 2 file systems so probably the solution you gave is at the moment the only one.
If you think about it, finally when copying file from local file system to s3, you need to have file content to put it in S3, so indeed it's not so wasteful in my opinion.
I solved it in the following way:
$contents = \File::get($destination);
\Storage::disk('s3')
->put($s3Destination,$contents);
Sometimes we don't get the data using $contents = Storage::get($file); - storage function so we have to give root path of the data using Laravel File instead of storage path using Storage.

Laravel uploading a file to the right directory

I upload the files alright. Only it turns out that, in my case, the starting directory is Public, and that results that the files are accesible by everybody when they should be in the directories that are protected by previous authentication, that is, under App.
So, when I do this:
$file = Input::file('fichero');
$destinationPath = 'uploads/folder';
$file->move($destinationPath);
// Create model
$model = new Model;
$model->filename = $file->getClientOriginalName();
$model->destinationPath = $destinationPath;
The file is saved in here: localh.../myweb/public/
and that is outside the App parent folder where all my application files are.
I can imagine that the fact that is going to that directory is because it is taking the default in the configuration file, which is localhost/laravel/public/index.php/
so, writing something like
$file->move(base_path().'/uploadfolder/subdirectory/', $newname);
will start from what is fixed as base_path, but the base_path needs to be something like localhost/mywebname/public.
So, how do you write the path so that it goes to directories under App and not under Public?
I hope I am not asking a dumb question, but I have been trying for days and I have read the 3 related queries in here.
thank you
You may use:
app_path() // Path to the 'app' folder
app_path('controllers/admin') // Path to the 'app/controllers/admin' folder
This will return you local file system path, you should put your uploaded images in public/... folder.

Laravel image upload creating folder instead of file

When I try to upload image from the form , Laravel is creating directory out of image name. Here is my basic code:
$file = Input::file("image");
$file = $file->move(public_path(). "/img/".$file->getClientOriginalName());
//file name is IPC_diagram.png
When die and dump I got this:
'/var/www/php/frbit/l4blog/public/img/IPC_diagram.png/phpvEb9zk'
Now name of image is name of new folder and image is renamed to some random php string, and placed in that folder.
What is the problem. Maybe something related to Linux specific handling of files. Also I was looking into symfony code for this, and symfony is trying to crete new folder every time file is moved but I don't understand how it is related to my code.
Thanks for help.
Do not use dot. Use comma, like this:
$name = time() . $file->getClientOriginalName();
$file->move('uploads/posts/',$name);

Resources