• 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
File Upload Validation Broke

#1
Question 
Although the infinite monkey theorem will surely solve this at some point… maybe… the monkeys are becoming restless.

Whilst attempting to have a file upload validated, no results other than the file->isValid() standard failure responses (that particular code is not shown here) or a consistent Validation error message (always fails the first check, regardless of what the check is) are obtainable…

It appears as though:
   1) getFile() always returns with a NULL
   2) getFiles() although returning an array of files (which contains only one in this case), the Validation routine can’t find the details in this array and fails

I’ve tried a variety of things to get the single file’s FileCollection() instance so that something like $validationResult = $validation->run($file, 'file_upload_valid'); could be tried, but nothing seems to work?

Any ideas on what’s broken?

Thanks.

HTML:
Code:
<form id="form_upload" class="form-horizontal" action="" method="post" accept-charset="utf-8" enctype="multipart/form-data">
   ...
   echo form_upload(['id' => 'files_upload', 'name' => 'file_name', 'class' => 'form-control', 'value' => '' ]);  // 'type' => 'file' done automatically

Controller:
Code:
<?php namespace App\Controllers;
   
    // use CodeIgniter\HTTP\RequestInterface;
    use App\Libraries\Functions;
    // use Config\Vars;

    class Upload extends BaseController {
     ...
       public function upload () {


            $file = $this->request->getFile('file_name');    // get the single file
               
            // echo $file->getName(); // RESULT:  Error - Call to a member function getName() on null if getFile() is used.  Obviously also fails with “Error - Call to a member function getName() on array” if getFiles() is used.

             print_r($file); // RESULT: NULL if getFile() is used. If getFiles() is used:  Array ( [8oauxy0g0zd31_jpg] => CodeIgniter\HTTP\Files\UploadedFile Object ( [path:protected] => C:\xa…
     
                  $validationResult = $this->validate('file_upload_valid');

                  echo($validation->getError('file_name')); // RESULT:  “Please select an upload file”, or if the first rule is removed, then the second rule fail error message…

Validation.php:
Code:
public $file_upload_valid = [
            'file_name' => [
                'label' => 'upload',
                'rules' => 'uploaded[file_name]|ext_in[file_name,jpg,jpeg,gif,png]|is_image[file_name]',
                'errors' => [
                    'uploaded' => 'Please select an {field} file',
                     ...

The upload is done via an AJAX script, that definitely uploads the file - the request payload looks as one would expect:

-----------------------------202171451529518883343801695575
Content-Disposition: form-data; name="tp_csrft"

7d039a403950a52c36d9e2b5562ec107
-----------------------------202171451529518883343801695575
Content-Disposition: form-data; name="8oauxy0g0zd31.jpg"; filename="8oauxy0g0zd31.jpg"
Content-Type: image/jpeg
ÿØÿà. . .

Some additional code (not shown above) moves the new upload to the writable/uploads directory (which does happen on every form submission).

Reply

#2
Huh  Any suggestions would be most welcome...
Reply

#3
Has anyone had validation working on file uploads? (as per the explanation given in the User Guide: https://codeigniter.com/user_guide/libra....html#id27) ?... I'm still having no joy in my attempts to get any of the listed rules working?

Thanks.
Reply

#4
(04-23-2020, 02:06 PM)Gary Wrote: Has anyone had validation working on file uploads?  (as per the explanation given in the User Guide:  https://codeigniter.com/user_guide/libra....html#id27) ?... I'm still having no joy in my attempts to get any of the listed rules working?

Thanks.

Hi, I decided to try a quick test (added validation to my existing image uploading stuff):

PHP Code:
        $validation = \Config\Services::validation();
        $validation->setRules(['images[]' => 'max_size[images[],700000]']);

        if ($this->request->getPost('submit')) {
            $validation->withRequest($this->request)->run();

            if(empty($validation->listErrors())) {
                $this->upload($table$album_id);
            }
        

and this is view:
PHP Code:
        echo \Config\Services::validation()->listErrors() ?? null;
        helper('form');
        echo form_open_multipart();
        echo form_label('Put up some pictures (up to 8 at a time)''images');
        echo form_upload('images[]'set_value('images[]'), 'multiple id="images"');
        echo form_submit('submit''Save');
        echo form_close(); 

No, not working. I get an error in my view file saying the image is too big. But it is definitely not too big.

Not having any problem with the getFile() function or getFiles(), without the validation stuff I just added, of course.

p.s. I'm not using ajax
You can see things I made with codeigniter here: itart.pro its not overly impressive as I have very little time to learn.
Reply

#5
Thanks for that Leo.

It consistently fails on whatever the first test is... regardless of how one sets up the validation (I've even tried the calling with the parameters directly (without setting the rules) as in the example: $this->validate(['avatar'=>'uploaded[avatar]|max_size[avatar,1024]']) with no success).

I see you're doing multiple files... which I've also resorted to using (with getFiles()), even for a single file (which I then "separate" out of the upload using foreach($files_upload as $file)...) which works. I've still not been able to get getFile() working for some reason.
Reply

#6
I upload single files also, try this simplified method in some controller:

PHP Code:
public function testing()
{
        if($this->request->getPost('save')) {
            $file $this->request->getFile('img_path');
            if ($file->isValid() && !$file->hasMoved())
            {
                $originalName $file->getName();
                $extension $file->getExtension();
                $source $file->getTempName();
                $type $file->getMimeType();
                $random $file->getRandomName();
                $file->move(WRITEPATH.'uploads'$random);
            }
        }

        helper('form');
        echo form_open_multipart();
        echo form_label('Upload file''img_path');
        echo form_upload('img_path'set_value('img_path'), 'id="img_path"');
        echo form_submit('save''Save the file');
        echo form_close();

You can see things I made with codeigniter here: itart.pro its not overly impressive as I have very little time to learn.
Reply

#7
Great, thanks for that Leo… your code worked, straight out the box.

Although it wasn’t too different to many of my previous hacks at getting getFile() working, it helped clarify things for me pretty quickly… primarily because the one notable departure from your working example code is that my file/s are uploaded using a JavaScript post (which, because they may be really large, permits an easy-to-implement % progress indication on the client-side).

So, although your code has identified where the getFile() problem is, thank you... I still haven’t found a way to get the JavaScript post working for a single file (other than stepping through the multiple file uploads array, as mentioned earlier).  When using getFile(), any existence and/or isValid() checks always fail because the expected POST/REQUEST/FILE that should contain an instance of the uploaded file never appears to get populated using getFile() if the post comes via a JavaScript post?!?

I have snooped around in the framework code a bit (particularly \system\HTTP\Files\FileCollection.php, and what it references), and see that although getFile() and getFiles() functions look similar, under the bonnet they are different- particularly in how they get the objects they return… possibly why getFiles() always returns something to work with, whereas getFile() always returns NULL for my JavaScript posts.

Without knowing enough, all I can guess is that there is something not quite right with getFile(), or perhaps getFiles()… although getFiles() works (and perhaps it shouldn’t ?).  I suspect that, to have a similar response, getFiles() should probably be using FileCollection->getFileMultiple() vs the FileCollection->all() it currently uses (?).  The only references to getFileMuliple() occur in IncomingRequest.php and FileCollection.php, and… as fate would have it… also in (the apparently non-functional) file Validation (in FileRules.php), but, other than that, doesn’t appear to be used by the framework.
Reply

#8
You know what, I got validation to work...still without JavaScript though - I don't have any progress showing things made, to test with.
PHP Code:
public function testing_uploads_validation()
    {
        $validation = \Config\Services::validation();
        $validation->setRule('img_path''Upload file''uploaded[img_path]|max_size[img_path,1000]|ext_in[img_path,jpg]');

        if($this->request->getPost('save')) {

            $validation->withRequest($this->request)->run();

            if(empty($validation->getErrors())) {
                $file $this->request->getFile('img_path');
                if ($file->isValid() && !$file->hasMoved())
                {
                    $random $file->getRandomName();
                    $file->move(WRITEPATH.'uploads'$random);
                }
            }
        }

        echo $validation->listErrors() ?? null;

        helper('form');
        echo form_open_multipart();
        echo form_label('Upload file''img_path');
        echo form_upload('img_path'set_value('img_path'), 'id="img_path"');
        echo form_submit('save''Save the file');
        echo form_close();
    
You can see things I made with codeigniter here: itart.pro its not overly impressive as I have very little time to learn.
Reply

#9
Great, thanks Leo.

I'll give the way you've done it a try.

I had thought I had it working at some point a few days ago (not done the way you have it now, though)... but it wasn't the main focus of attention at the time, and when I came back to work on this portion of the code again, it was broke... and I can't be sure whether it was the code that was working, or the red wine.

As an aside, I see you have a helper('form') fetish... and there's probably no harm in that, though, if you have a look in the user manual (under Form Helper), it says this: If you use any of the form helper functions listed on this page, and you pass values as an associative array, the form values will be automatically escaped, so there is no need to call this function. Use it only if you are creating your own form elements, which you would pass as strings.
Reply

#10
Also, this may or may not help you. I have this set up currently for a wysiwyg plugin. I didn't have the time to fiddle with it, it just uses native PHP and works with csrf on. I just tested it out, to make sure I won't have to go through what you are goin through now Smile its simpler then a "progress bar" type thing, it works for multiple files upload.


Javascript
Code:
$('.wysiwyg').summernote({
            //////irrelevant wysiwyg summer note stuff////no time to polish the function below to be stand alone/////
            callbacks: {
                onImageUpload: function (files) {
                    for (var i = 0; i < files.length; i++) {
                        send_wysiwyg(files[i]);
                    }
                }
            }
        });

function send_wysiwyg(file) {
        if (file.type.includes('image')) {
            var name = file.name.split(".");
            name = name[0];
            var data = new FormData();
            data.append('file', file);
            data.append("csrf_token", "<?=csrf_hash()?>");
            $.ajax({
                url: '/media/wysiwyg_upload',
                type: 'POST',
                contentType: false,
                cache: false,
                processData: false,
                dataType: 'JSON',
                data: data,
                headers: {'X-Requested-With': 'XMLHttpRequest'},
                success: function (response) {
                    if (response['status'] === 'success') {
                        $('.wysiwyg').summernote('insertImage', response['url'], name);
                        update_csrf_fields(response.csrf_token);
                    } else {
                        console.log(response['status']);
                    }
                }
            })
                .fail(function (e) {
                    console.log(e);
                });
        } else {
            console.log("That's not an image");
        }
    }

Controller:
PHP Code:
public function wysiwyg_upload()
    {
        if ($this->request->isAJAX()) {
            $confirm_csrf $this->request->getRawInput();
            if (isset($_FILES)) {
                if (!$_FILES['file']['error']) {
                    if (preg_match("/image/"$_FILES['file']['type'])) {
                        $name time() . rand(100999);
                        $ext explode('.'$_FILES['file']['name']);
                        $filename $name '.' $ext[1];
                        $destination UPLOAD_FILES $filename;
                        $location $_FILES["file"]["tmp_name"];
                        move_uploaded_file($location$destination);
                        $response['url'] = STATIC_FILES 'uploads/' $filename;
                        $response['status'] = 'success';
                        
$response['csrf_token'] = csrf_hash();
                    } else {
                        $response['status'] = 'Its not an image';
                    }
                } else {
                    $response['status'] = "The image couldn't load. Error(" $_FILES['file']['error'] . ")";
                }
            } else {
                $response['status'] = "There is no spoon.....file.";
            }

            return $this->response->setJSON($response);
        }
        return '{"error":"Invalid Request"}';
    

P.S. the form wouldn't generate without the helper.  I mean the functions below helper('form'); in my example wouldn't work.
P.P.S. I'll try to convert native PHP to use codeigniter func's to test for bugs, later on, to see if I get getFile() errors like you.
You can see things I made with codeigniter here: itart.pro its not overly impressive as I have very little time to learn.
Reply


Digg   Delicious   Reddit   Facebook   Twitter   StumbleUpon  


  Theme © 2014 iAndrew  
Powered By MyBB, © 2002-2020 MyBB Group.