pjk_ok
pjk_ok

Reputation: 967

Limit File Uploads To One File - PHP

I have a file input element that is used to upload a profile image to a user profile page. By default browsers only allow this element to upload one file unless you add the multiple attribute.

I'd like to set a back up in the PHP though just in case someone decides to add the 'multiple' attribute in the HTML.

I thought assigning the $_POST superglobal to a variable and having an if statement saying if this value is greater than 1 would be prevent this, but it doesn't?

What is the best way to approach this? I've tried various things such as the count() array method but can't seem to find a solution to what seems like a very simple problem?

if(isset($_POST['submit-profile-image'])) {

    $profileImage = $_POST['submit-profile-image'];

    if (isset($profileImage)) {
        if ($profileImage > 1) {
            $error[] = "You cannot upload more than one profile image";
        }
    }
    // ALL OTHER CODE
}

I've also tried using the $_FILES superglobal and counting the instances of the $_FILES['profile-image']. This encounters a different problem in that it blocks more than one file upload BUT also blocks single file uploads (and I don't understand why)?

if(isset($_FILES['profile-image'])){
    if(count($_FILES['profile-image']) > 1){
        $error[] = "You cannot upload more than one profile image";
    }
}

Upvotes: 1

Views: 706

Answers (1)

You can't prevent a user sending multiple files. All you can do is defend against the possibility, and fail gracefully.

Let's assume that your HTML includes this <form>

<form method='post' enctype="multipart/form_data">
  <input type='file' name='uploadFile'>
  <input type='submit' name='submit'>
</form>

When the user selects a file and clicks submit the browser packs up the file and sends it, PHP unpacks the file to the server disk, and then presents the file details to your program in the $_FILES['uploadFile'] array.

If we assume that the user edits your HTML and adds multiple then the browser will pack up the files and send them. PHP will unpack the first file and add its detail to $_FILES['uploadFile'] as before. It will then unpack the second file and place its details in $_FILES['uploadFile'], overwriting the first file. Your program sees only one file, knows nothing of any other file, and carries on.

To get two files your user will have to change the name of the file input to use array syntax, so lets suppose he changes the line to

  <input type='file' name='uploadFile[]' multiple>

Now PHP unpacks the file details into a set of arrays. Instead of having $_FILES['uploadFile']['name'] containing a string with one filename, it becomes an array of strings.

It is likely that your code, expecting a string, will choke on an array and fail in some unexpected way. You can check for this condition with

if (is_array($_FILES['uploadFile']['name'])) {throw new Exception("Too many files");}

So, our user, determined to force this extra file on you now adds a second <input> to the form:

<form method='post' enctype="multipart/form_data">
  <input type='file' name='uploadFile'>
  <input type='file' name='uploadFile'>
  <input type='submit' name='submit'>
</form>

The second file overwrites the first as it has the same name. Your program is none the wiser and carries on with just one file. So the user changes the name on the second input:

<form method='post' enctype="multipart/form_data">
  <input type='file' name='uploadFile'>
  <input type='file' name='uploadFileB'>
  <input type='submit' name='submit'>
</form>

You could check for this by looking at count($_FILES), but your program isn't looking for a second input, so it will ignore it anyway and carry on handling just the first file. If the user also changes the first name your program won't see any files, and if he reverses the names, your program will see just the second file and ignore the first.

Alternatively, set the PHP configuration value in PHP.INI:

max_file_uploads = 1;

If you do this, PHP will ignore the second and subsequent files. Your code will still have to deal with the naming issues. Setting this with ini_set() doesn't seem to have any effect.

Upvotes: 2

Related Questions