Currently have working on an image upload in a Laravel/Inertia/React stack.
Issue: It currently works using a symbolic link to the public folder. This is no good as it means anyone can access the images if they have the link. How can I do this differently so that only the owner of the image can view it?
Displaying the image:
<img src={`/storage/${props.company.logo_filename}`}/>
Storing the logo:
public function storeLogo(Request $request)
{
$company = auth()->user()->company();
$logo = $request->file('logo');
$extension = $logo->getClientOriginalExtension();
Storage::disk('public')->put($logo->getFilename() . '.' . $extension, File::get($logo));
dd(Storage::url('public/'. $logo->getFilename()));
$company->logo_mime = $logo->getClientMimeType();
$company->logo_original_filename = $logo->getClientOriginalName();
$company->logo_filename = $logo->getFilename() . '.' . $extension;
$company->save();
//DELETE old Logo if exists
return Redirect::back()->with(
'message',
[
'type' => 'success',
'text' => 'Logo Updated'
]
);
}
note: In this particular scenario (company logos) I'm aware that it probably doesn't matter if someone else gets access to the images. Please just assume they are sensitive.
You could add a route to serve the files and check if the current user is allowed to access the file.
use Illuminate\Support\Facades\Storage;
Route::get('files/{filename}', function (string $filename) {
// Do your magic to check if the current user is allowed to access that file
// Do your magic to determine the full path of the file
$fullPath = '/some/directory/' . $filename;
// Once we have the full path we can stream the contents of the file to the client
return Storage::response($fullPath);
});
Unfortunately the Storage::response() method is undocumented as far as I know, but you can find it's implementation here:
https://github.com/laravel/framework/blob/5.5/src/Illuminate/Filesystem/FilesystemAdapter.php
Related
On any particular View, if I include this link:
<?php echo $this->Html->link('Download', ['controller' => 'Uploads', 'action' => 'download',$upload->id]) ?>
A user can download a file based on the $upload->id provided.
I'm using this as my download function in my Uploads controller:
$upload = $this->Uploads->find('all')->first();
$filePath = '\xampp\htdocs\project\webroot\uploads'; //file path to where the uploaded file is
$this->response->file($filePath . DS . $upload->name,
array('download' => true, 'name' => $upload->name)); //finds the corresponding name attribute in the Uploads table of the database, finds a matching name in the uploads directory and then downloads that file.
$this->set(compact('upload'));
However, I was wondering if there was a way to be able to have the file path be more flexible. In my config.php, I have set two variables called $host and $basepath to refer to the web host (localhost) and the project name (project) respectively. I pass these variables to the AppController, and they can be accessed by any View.
But I can't seem to use these variables in the $filePath variable of the download function, so if I or anyone else were to change hosts or the project name, instead of being able to simply change config.php, I or anyone else would have to go and find this and change it as well (and if I had multiple download functions in multiple controllers, they'd have to go and change each and every one of them).
Take a look at the CakePHP global constants. You can easily access the path to your webroot by using WWW_ROOT.
https://book.cakephp.org/3.0/en/core-libraries/global-constants-and-functions.html
Please help me out here because I can't belive my eyes.
I refuse to use some 3rd party plugin for file uploads, and refuse to creat a separate entity for a file/document. I just want simple file upload that I would do in Zend/Laravel and so on.
I have a invoice table with the last collumb name "attachment", I want to store here its sanitized name (eg: 123421_filename.jpg ), the addition form and upload went well.
code here:
//AddAction
$file=$form['attachment']->getData();
$fileName=$file->getClientOriginalName();
$sanitizedFilename=rand(1, 99999).'_'.$fileName;
$dir='files/'.$userId.'/';
$file->move($dir, $sanitizedFilename);
...
$invoice->setAttachment($sanitizedFilename);
$em = $this->getDoctrine()->getManager();
$em->persist($invoice);
First problem I have and don't know how to solve is using the edit form.. I have a formbuilder
$builder->add('attachment', 'file',array('data_class'=>null))
Because I don't have a "Object" for my file and because in my invoice table I store the name as a string, I get an error here and I need to force the data_class => null .. which is bad, because if I edit a Invoice in the frontend for the upload field I get NULL, instead of the filename of the current file linked to the invoice.
With:
$builder->add('attachment', 'text')
I don't get the file input just a silly textbox BUT THIS TIME --with the name in it-- .. so how do I solve this? File Input widget and file name in it without a Document object ?!
And the second question ( I am merely at the point of throwing out all my application developed until now and move to laravel because I had a lot of "these kind of problems" ..doing simple things in symfony is almost always more complicated than in other frameworks..or maybe its my fault that I don't want to folow the guidelines and create a Document Entity?! What if I want flexibility and want to manage my database in another manner that doesn't fit in all these guidelines?)
So I am on the edit form action, I upload a new file (don't forget that I have the file set to $builder->add('attachment',file',array('data_class'=>null))
I can't get my attachment string name from the current invoice I am editing? !
public function editAction($id)
....
$invoice = $em->getRepository('AppBundle:Invoice')->find($id);
..
if ($form->isValid()) {
$oldFileName=$invoice->getAttachment(); //this is null
$oldFileName=$invoice->getId(); //this returns the invoice id
$oldFileName=$invoice->getValue(); //returns invoice value
echo 'old: '.$oldFileName.'<br/>';
exit();
}
So someone please tell me why I can't access my invoices property? Which is a string ?
I tried making a new instance I though that somehow if I create the form with the $invoice object it somehow links him to the attachment from the edit form
if ($form->isValid()) {
$sameInvoice= $em->getRepository('AppBundle:Invoice')->find(20); //hardcoded ID
$oldFileName=$sameInvoice->getAttachment(); //still null
$oldFileName=$sameInvoice->getId(); //returns 20
echo 'old: '.$oldFileName.'<br/>';
exit();
The only thing I wanted was, have a filename string in my invoice table, and test myself if the file exists in the path with that filename and if it exists, then delete it and upload the new one and so on.. why does it have to be so hard ??
Why do I have to create a entity ? Why do I have to alter my database structure (its not the case here..but what if the client doesn't want the database to be changed..so I can't insert this "Document" table).. Why can't the form builder show the data from invoice->attachment, I get it that because he needs a file data and he can't accept a string, but why aren't there guidelines for these simple tasks?!
Alright I finally managed to get it working:
So the problem why it was NULL was because it did bind the $invoice instance to the form:
$invoice = $em->getRepository('AppBundle:Invoice')->find($id);
$form = $this->createForm(new InvoiceType($id), $invoice);
After this block of code, all refferences like : $invoice->getAttachment();
Are reffered to as the form invoice object not the actual object, so basically after this I can only call things like: "getClientOriginalName()" and would show me the new file that I uploaded in the edit form.
To counter this I created a variable that stores that name initially before the $invoice object to get bind to the form
$invoice = $em->getRepository('AppBundle:Invoice')->find($id);
$oldFileName=$invoice->getAttachment(); //this is stil the object here I can see the record from the database
$form = $this->createForm(new InvoiceType($id), $invoice);
//here the $invoice->getAttachment(); returns null
So now I have the old name and I can test it like:
if ($form->isValid()) {
$fs=new Filesystem();
$newFile=$form['attachment']->getData();
$newFileName=$newFile->getClientOriginalName();
if($newFileName != $oldFileName)
{
// if($fs->exists('files/'.$this->getUser()->getId().'/'.$oldFileName))
// $fs->remove('files/'.$this->getUser()->getId().'/'.$oldFileName);
$sanitizedFilename=rand(1, 99999).'_'.$newFileName;
$dir='files/'.$this->getUser()->getId().'/';
$newFile->move($dir, $sanitizedFilename);
$invoice->setAttachment($sanitizedFilename);
}
$em->persist($invoice);
$em->flush();
return $this->redirect($this->generateUrl('my-invoices'));
}
For the other problem with the filename of the old file not appearing in the Edit Invoice Page I sent my variable to the view :
return $this->render('AppBundle:Invoices:edit.html.twig', array(
'oldFileName' => $oldFileName)
And put that value to the label of the input file widget using twig
Remain calm. You are making this much harder then it needs to be.
myControllerAction()
{
// No need for an object, an array works fine
$model = array(
'invoice' => $invoice,
'attachment' => null
);
$builder = $this->createFormBuilder($model);
$builder->setAction($this->generateUrl('cerad_game_schedule_import'));
$builder->setMethod('POST');
$builder->add('attachment', 'file');
$builder->add('invoice', new InvoiceType());
$builder->add('import', 'submit', array(
'label' => 'Import From File',
'attr' => array('class' => 'import'),
));
$form = $builder->getForm();
$form->handleRequest($request);
if ($form->isValid())
{
$model = $form->getData();
$file = $model['attachment']; // The file object is built by the form processor
if (!$file->isValid())
{
$model['results'] = sprintf("Max file size %d %d Valid: %d, Error: %d<br />\n",
$file->getMaxFilesize(), // Returns null?
$file->getClientSize(),
$file->isValid(),
$file->getError());
die($model['results'];
}
$importFilePath = $file->getPathname();
$clientFileName = $file->getClientOriginalName();
// Xfer info to the invoice object and update
}
I am in a problem with CakePDF. I am using CakePHP 2.3.6. I setup everything alright, bootstrap & core files are ok.
Now, I want to generate many pdf files and download them. Downloading is ok, but when I download the files, I see that the generated pdf files say that Undefined $data, where I send $data to the view file.
Here is my Controller code:
App::uses('CakePdf', 'CakePdf.pdf');
// ...
public function createPdf($id) {
$data = $this->DBTable->find('all', array(
'conditions' => array(
'DBTable.id' => $id
)
));
$CakePdf = new CakePdf();
$path = APP . DS . 'pdf' . DS;
foreach($data as $key => $var) {
$CakePdf->template('create', 'default');
$this->set('var', $var);
$this->pdfConfig = array(
'orientation' => 'portrait',
'filename' => $var['id'] . ".pdf"
);
$CakePdf->write($path . $var['id'] . ".pdf");
}
}
Here, I am downloading the files some other way, not related to this problem, because downloading is ok. When I run this, all files are downloaded, and when I see the files, they say "Udefined var", looks like they didn't receive and variable called "$var".
I put the view file & the layout exactly where ceeram said.
What should I do now ? Please help me.
At last I got a solution to this. In the Controller function :
$data=$this->ModelName->find('all');
$CakePdf=new CakePdf();
$CakePdf->template('view_file','default');
$this->pdfConfig=array('orientation'=>'portrait','filename'=>$data['name']);
$CakePdf->viewVars(array('data'=>$data));
$CakePdf->write($path.$data['name'].".pdf");
Here, we have to store view_file.ctp in View/Pdf/, and default in View/Layouts/pdf/, where view_file.ctp is the view file which we want to convert in pdf, and default is the layout for this specific view file.
Here, viewVars() function passes the data to the view file, just like :
$this->set->('data',$data);
Let me know if anything is not clear here.
Thanks.
I'm new to cakephp and I have found this milesjohnson's upload plugin ,
and I kind of like it, mostly because it gives me the chance of renaming the file once uploaded.Unfortunately, I can't get it to upload any zip/rar files.
This is the action where I upload the file:
function add() {
if (!empty($this->data)) {
if ($data = $this->Uploader->upload('link_referencia', array('name' => date("dmYhis")))) {
debug($data);
$this->data['Publicacione']['link_referencia']=$data['name'];
}
$this->Publicacione->create();
if ($this->Publicacione->save($this->data)) {
$this->Session->setFlash(__('The publicacione has been saved', true));
//$this->redirect(array('action' => 'index'));
} else {
$this->Session->setFlash(__('The publicacione could not be saved. Please, try again.', true));
}
}
$users = $this->Publicacione->User->find('list');
$this->set(compact('users'));
}
And this is the error I get everytime I attempt to upload any zip/rar file :
EDIT
Full insert query:
INSERT INTO `publicaciones` (`vigencia`, `tipo`, `titulo`, `descripcion`, `fecha_publicacion`, `fecha_caducidad`, `link_referencia`, `modified`, `created`) VALUES (1, 'c', 'there\'s nothing you can\'t do', '
fsdfsdfsdf
', '2011-06-07', '2011-06-30', Array, '2011-06-07 16:47:23', '2011-06-07 16:47:23')
Does anyone have any ideas on what the problem might be?
Thanks in advance.
i think there is nothing with the code what you have written but you should look at in to the plugin and find out whether it gives permission you to upload zip or not.
there will be conditions in your pluggin that you can upload only some kind of files like jpg,png,txt something like that.
I hope that helps you.
Regards,
Archit
Are you totally sure your File input on the form has type=>file specified, and same for your form?
Also I would look at other uploaders - MeioUpload can allow renaming and so on and is a bit more up to date. There is also the Cuploadify plugin (uploadify for cake) you can find on github.
Make sure your form is set to multipart/form-data like so:
<?php echo $this->Form->create('File', array('enctype' => 'multipart/form-data')); ?>
The following code will upload a file:
if ($this->request->is('post')) {
if ( $this->data['File']['file']['error'] <= 0 && $this->data['File']['file']['size'] <= 8388608 ) { // Check for no errors and that File size is around 8mb.
$folder = new Folder (ROOT . DS . 'app' . DS . 'filestorage' . DS, true); // create folder in /app/filestorage/
$path = $folder->path . $this->data['File']['file']['name']; // Set path to newly created folder + uploaded file name.
$tmpUrl = new File ( $this->data['File']['file']['tmp_name'] ); // Create temporary file object
if ($tmpUrl->copy($path , true) ) { // If copying file to path is successful,
$this->Session->setFlash(__('File uploaded succesfully!'));
}
}
I wanted to know if I was uploading an image in CodeIgniter to a database what would be my
$config['upload_path']
All the examples I have seen are using the filesystem. I have articles in a db and would like to store images relating to articles in the db as well. Can anyone help?
You can read this great article called Storing Images in Mysql.
The article covers the following:
Isn’t this a bad idea?
What is a BLOB?
Creating an image table
The upload form
Uploading the image
The upload() function
Display an image from the database
Displaying all the information
But not to leave you empty handed, look into Blob, it's a data-type for colums in MySQL ( and various other dbms ). This will let you store data such as Images and other binary file-types.
The idea of storing files and images in the database is in general the same as storing them on the filesystem, the layer in-between upload and having the actual file is just different.
You cannot just set your upload-path and hope everything is solved, you need to get some dirt on your hands aswell!
i posted here in hope this help someone since this is an old post and the answers are no help at all, this code works for me, also the model part is not here so you must figure it out reading codeigniter's docs , i think this will work if you put it in your controller, also it think the submit form must be pointing this function
function upload() {
$caption = $this->input->post('caption');
$codigo = $this->input->post('codigo');
//$imagen = $this->input->post('imagen');
$config['upload_path'] = 'uploads';// this is a directory with 777 permissions where you upload the file
$config['allowed_types'] = 'gif|jpg|jpeg|png|pdf';
//$config['max_size'] = '5000';
$this->load->library('upload', $config);
if (!$this->upload->do_upload('imagen')) { // this is the input from the form
echo $this->upload->display_errors();
} else {
//here $file_data receives an array that has all the info
//pertaining to the upload, including 'file_name'
$file_data = $this->upload->data();
$fp = fopen($file_data['full_path'], 'r');
$content = fread($fp, filesize($file_data['full_path']));
//$content = addslashes($content);
fclose($fp);
$data = array( // this is the table i got in my db
'idBotones' => null,
'imagen' => $content, //blob image
'caption' => $caption,
'codigo' => $codigo
);
$this->load->model('generic_model');
$table = "botones";
$this->generic_model->insertar_datos($table, $data);
//print_r($content);
}
}
It seems like for most common use cases storing images in the database is not a great idea.
Please see these two previous SO threads:
To Do or Not to Do: Store Images in a Database
Storing Images in DB - Yea or Nay?