Skip to content

Student Listing - Adding File Upload Capability

Encapsulating our File Handling Using Functions

We're going to be performing the same file handling operations multiple time, so we should take advantage of encapsulation and create a separate script for dealing with our file handling needs for our Student Listing application.

However, before we do that, we have some constant values we plan on using again, it's a good idea to create a file to hold these as constants as well:

studentlistingfileconstants.php - Student Listing: File Handling Constatns

1
2
3
4
5
<?php
// Student Listing File Constants
define('SL_UPLOAD_PATH', 'images/');
define('SL_MAX_FILE_SIZE', 524288);
define('SL_DEFAULT_STUDENT_FILE_NAME', 'generic_student.jpg');

Now let's create a script called studentimagefileutil.php with functions that will validate an uploaded student image file, move it from the temporary location to the images/ folder if there are no errors, and remove a student image file:

studentimagefileutil.php - Student Listing: File Handling Functions

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
<?php
require_once 'studentlistingfileconstants.php';

/**
 * Purpose:         Validates an uploaded student image file
 *
 * Description:     Validates an uploaded student image file is not greater than SL_MAX_FIE (1/2 MB),
 *                  and is either a jpg or png image type, and has no errors. If the image file
 *                  validates to these constraints, an error message containing an empty string is
 *                  returned. If there is an error, a string containing constraints the file failed
 *                  to validate to are returned.
 *
 * @return string   Empty if validation is successful, otherwise error string containing
 *                  constraints the image file failed to validate to.
 */
function validateStudentImageFile()
{
    $error_message = "";

    // Check for $_FILES being set and no errors.
    if (isset($_FILES) && $_FILES['student_image_file']['error'] == UPLOAD_ERR_OK)
    {
        // Check for uploaded file < Max file size AND an acceptable image type
        if ($_FILES['student_image_file']['size'] > SL_MAX_FILE_SIZE)
        {
            $error_message = "The student file image must be less than " . SL_MAX_FILE_SIZE . " Bytes";
        }

        $image_type = $_FILES['student_image_file']['type'];

        if ($image_type != 'image/jpg' && $image_type != 'image/jpeg' && $image_type != 'image/pjpeg'
            && $image_type != 'image/png' && $image_type != 'image/gif')
        {
            if (empty($error_message))
            {
                $error_message = "The student file image must be of type jpg, png, or gif.";
            }
            else
            {
                $error_message .= ", and be an image of type jpg, png, or gif.";
            }
        }
    }
    elseif (isset($_FILES) && $_FILES['student_image_file']['error'] != UPLOAD_ERR_NO_FILE
        && $_FILES['student_image_file']['error'] != UPLOAD_ERR_OK)
    {
        $error_message = "Error uploading student image file.";
    }

    return $error_message;
}

/**
 * Purpose:         Moves an uploaded student image file to the SL_UPLOAD_PATH (images/) folder and
 *                  return the path location.
 *
 * Description:     Moves an uploaded student image file from the temporary server location to the
 *                  SL_UPLOAD_PATH (images/) folder IF a student image file was uploaded and returns
 *                  the path location of the uploaded file by appending the file name to the
 *                  SL_UPLOAD_PATH (e.g. images/student_image.png). IF a student image file was NOT
 *                  uploaded, an empty string will be returned for the path.
 *
 * @return string   Path to student image file IF a file was uploaded AND moved to the SL_UPLOAD_PATH
 *                  (images/) folder, otherwise and empty string.
 */
function addStudentImageFileReturnPathLocation()
{
    $student_file_path = "";

    // Check for $_FILES being set and no errors.
    if (isset($_FILES) && $_FILES['student_image_file']['error'] == UPLOAD_ERR_OK)
    {
        $student_file_path = SL_UPLOAD_PATH . $_FILES['student_image_file']['name'];

        if (!move_uploaded_file($_FILES['student_image_file']['tmp_name'], $student_file_path))
        {
            $student_file_path = "";
        }
    }

    return $student_file_path;
}

/**
 * Purpose:         Removes a file given a path to that file.
 *
 * Description:     Removes the file referenced by $student_file_path. Supresses error
 *                  if file cannot be removed.
 *
 * @param $student_file_path
 */
function removeStudentImageFile($student_file_path)
{
    @unlink($student_file_path);
}

Modifications to addstudent.php

Modify the Form to Allow Uploads

In order to allow file upload capabilities, we need to make a couple of modifications to our form. Let's head back over to the lecture notes to discuss this.

Make the following modifications to the Add a Student form to allow a user to upload an image file:

addstudent.php - Add Student: Add File Upload Capability to Form

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
<form enctype="multipart/form-data" class="needs-validation"
      novalidate method="POST" action="<?= $_SERVER['PHP_SELF'] ?>">
  ...
  <div class="form-group row">
    <label for="student_image_file" class="col-sm-3 col-form-label-lg">
            Student Image File</label>
    <div class="col-sm-8">
      <input type="file" class="form-control-file"
              id="student_image_file" name="student_image_file">
    </div>
  </div>
  <button class="btn btn-primary" type="submit" 
          name="add_student_submission">Add Student</button>
</form>

Uploading the File

After the user submits the form data by selecting the Add Student button , the form data is available within the if block checking to see if the form data is set in the $_POST superglobal variable:

1
2
3
if (isset($_POST['add_student_submission'], $_POST['student_first_name'],
          $_POST['student_last_name'], $_POST['student_email']))
{

We want to validate the file inside this if block, moving it to the images/ folder, and save the path to the database table. Make the following modifications to addstudent.php:

addstudent.php - Add Student: Add Upload File Modificaitons

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
if (isset($_POST['add_student_submission'], $_POST['student_first_name'],
          $_POST['student_last_name'], $_POST['student_email']))
{
    require_once('dbconnection.php');
    require_once('studentimagefileutil.php');

    $student_first_name = $_POST['student_first_name'];
    $student_last_name = $_POST['student_last_name'];
    $student_email = $_POST['student_email'];

    /*
    Here is where we will deal with the file by calling validateStudentImageFile().
    This function will validate that the student image file is not greater than 128
    characters, is the right image type (jpg/png/gif), and not greater than 512KB.
    This function will return an empty string ('') if the file validates successfully,
    otherwise, the string will contain error text to be output to the web page before
    redisplaying the form.
    */
    $file_error_message = validateStudentImageFile();

    if (empty($file_error_message))
    {

        $dbc = mysqli_connect(DB_HOST, DB_USER, DB_PASSWORD, DB_NAME)
                or trigger_error(
                        'Error connecting to MySQL server for' . DB_NAME, 
                        E_USER_ERROR);

        $student_image_file_path = addStudentImageFileReturnPathLocation();

        $query = "INSERT INTO studentListing (first_name, last_name, email, image_file) " 
              . " VALUES ('$student_first_name', '$student_last_name', "
              . "'$student_email', '$student_image_file_path')";

        mysqli_query($dbc, $query)
            or trigger_error(
                'Error querying database studentListing: Failed to insert student data',
                E_USER_ERROR);

        $display_add_student_form = false;
?>
    <h3 class="text-info">The Following Student Details were Added:</h3><br/>

    <h1><?= "$student_first_name $student_last_name"?></h1>
    <div class="row">
      <div class="col-2">
        <img src="<?= $student_image_file_path ?>" class="img-thumbnail" style="max-height: 200px;" alt="Student image">
      </div>
      <div class="col">
        <table class="table table-striped">
          <tbody>
          <tr>
              <th scope="row">First Name</th>
              <td><?= $student_first_name ?></td>
          </tr>
          <tr>
              <th scope="row">Last Name</th>
              <td><?= $student_last_name ?></td>
          </tr>
          <tr>
              <th scope="row">Email</th>
              <td><?= $student_email ?></td>
          </tr>
          </tbody>
        </table>
      </div>
    </div>
    <hr/>
    <p>Would you like to <a href='<?= $_SERVER['PHP_SELF'] ?>'> add another student</a>?</p>
<?php
    }
    else
    {
        // echo error message
        echo "<h5><p class='text-danger'>" . $file_error_message . "</p></h5>";
    }
}

Finally, we want to make the form fields sticky in case there is an error (as you see handled in the else case above), and the user needs to resubmit the form because of a file upload error. Make the following modifications to the form in addstudent.php:

First add variables for the fields we want to make sticky and set them to empty strings:

addstudent.php - Add Student: Make Form Fields sticky - add variables

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
<hr/>
<?php
    $display_add_student_form = true;

    $student_first_name = "";
    $student_last_name = "";
    $student_email = "";

    // pro-tip: you can test multiple variables within a single isset() call
    if (isset($_POST['add_student_submission'], $_POST['student_first_name'],
              $_POST['student_last_name'], $_POST['student_email']))
    {

addstudent.php - Add Student: Make Form Fields sticky - add variables to value attribute

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
<form enctype="multipart/form-data" class="needs-validation"
      novalidate method="POST" action="<?= $_SERVER['PHP_SELF'] ?>">
  <div class="form-group row">
    <label for="student_first_name"
            class="col-sm-3 col-form-label-lg">First Name</label>
    <div class="col-sm-8">
      <input type="text" class="form-control" id="student_first_name" 
              name="student_first_name" value= '<?=$student_first_name?>'
              placeholder="First Name" required>
      <div class="invalid-feedback">
        Please provide a valid first name.
      </div>
    </div>
  </div>
  <div class="form-group row">
    <label for="student_last_name" 
            class="col-sm-3 col-form-label-lg">Last Name</label>
    <div class="col-sm-8">
      <input type="text" class="form-control" id="student_last_name" 
              name="student_last_name" value= '<?=$student_last_name?>'
              placeholder="Last Name" required>
      <div class="invalid-feedback">
        Please provide a valid last name.
      </div>
    </div>
  </div>
  <div class="form-group row">
    <label for="student_email" 
            class="col-sm-3 col-form-label-lg">Email</label>
    <div class="col-sm-8">
      <input type="email" class="form-control" 
              id="student_email" 
              name="student_email" value= '<?=$student_email?>'
              placeholder="Email" required>
      <div class="invalid-feedback">
        Please provide a valid email address.
      </div>
    </div>
  </div>
  <div class="form-group row">
    <label for="student_image_file" class="col-sm-3 col-form-label-lg">
            Student Image File</label>
    <div class="col-sm-8">
      <input type="file" class="form-control-file"
              id="student_image_file" name="student_image_file">
    </div>
  </div>
  <button class="btn btn-primary" type="submit" 
          name="add_student_submission">Add Student</button>
</form>

Complete Code Listing

addstudent.php - Add Student: Complete Code Listing (with File Upload)

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
<!DOCTYPE html>
<html>
  <head>
    <title>Add Student</title>
    <link rel="stylesheet"
          href="https://stackpath.bootstrapcdn.com/bootstrap/4.2.1/css/bootstrap.min.css"
          integrity="sha384-GJzZqFGwb1QTTN6wy59ffF1BuGJpLSa9DkKMp0DgiMDm4iYMj70gZWKYbI706tWS"
          crossorigin="anonymous">
  </head>
  <body>
    <div class="card">
      <div class="card-body">
        <h1>Add a Student</h1>
        <nav class="nav">
          <a class="nav-link" href="index.php">Students</a>
        </nav>
        <hr/>
        <?php
            $display_add_student_form = true;

            $student_first_name = "";
            $student_last_name = "";
            $student_email = "";

            // pro-tip: you can test multiple variables within a single isset() call
            if (isset($_POST['add_student_submission'], $_POST['student_first_name'],
                      $_POST['student_last_name'], $_POST['student_email']))
            {
                require_once('dbconnection.php');
                require_once('studentimagefileutil.php');

                $student_first_name = $_POST['student_first_name'];
                $student_last_name = $_POST['student_last_name'];
                $student_email = $_POST['student_email'];

                /*
                Here is where we will deal with the file by calling validateStudentImageFile().
                This function will validate that the student image file is not greater than 128
                characters, is the right image type (jpg/png/gif), and not greater than 512KB.
                This function will return an empty string ('') if the file validates successfully,
                otherwise, the string will contain error text to be output to the web page before
                redisplaying the form.
                */
                $file_error_message = validateStudentImageFile();

                if (empty($file_error_message))
                {

                    $dbc = mysqli_connect(DB_HOST, DB_USER, DB_PASSWORD, DB_NAME)
                            or trigger_error(
                                    'Error connecting to MySQL server for' . DB_NAME, 
                                    E_USER_ERROR);

                    $student_image_file_path = addStudentImageFileReturnPathLocation();

                    $query = "INSERT INTO studentListing (first_name, last_name, email, image_file) " 
                          . " VALUES ('$student_first_name', '$student_last_name', "
                          . "'$student_email', '$student_image_file_path')";

                    mysqli_query($dbc, $query)
                        or trigger_error(
                            'Error querying database studentListing: Failed to insert student data',
                            E_USER_ERROR);

                    $display_add_student_form = false;
            ?>
                <h3 class="text-info">The Following Student Details were Added:</h3><br/>

                <h1><?= "$student_first_name $student_last_name"?></h1>
                <div class="row">
                  <div class="col-2">
                    <img src="<?= $student_image_file_path ?>" class="img-thumbnail" style="max-height: 200px;" alt="Student image">
                  </div>
                  <div class="col">
                    <table class="table table-striped">
                      <tbody>
                      <tr>
                          <th scope="row">First Name</th>
                          <td><?= $student_first_name ?></td>
                      </tr>
                      <tr>
                          <th scope="row">Last Name</th>
                          <td><?= $student_last_name ?></td>
                      </tr>
                      <tr>
                          <th scope="row">Email</th>
                          <td><?= $student_email ?></td>
                      </tr>
                      </tbody>
                    </table>
                  </div>
                </div>
                <hr/>
                <p>Would you like to <a href='<?= $_SERVER['PHP_SELF'] ?>'> add another student</a>?</p>
            <?php
                }
                else
                {
                    // echo error message
                    echo "<h5><p class='text-danger'>" . $file_error_message . "</p></h5>";
                }
            }

            if ($display_add_student_form)
            {
        ?>
        <form enctype="multipart/form-data" class="needs-validation"
              novalidate method="POST" action="<?= $_SERVER['PHP_SELF'] ?>">
          <div class="form-group row">
            <label for="student_first_name"
                   class="col-sm-3 col-form-label-lg">First Name</label>
            <div class="col-sm-8">
              <input type="text" class="form-control" id="student_first_name" 
                     name="student_first_name" value= '<?=$student_first_name?>'
                     placeholder="First Name" required>
              <div class="invalid-feedback">
                Please provide a valid first name.
              </div>
            </div>
          </div>
          <div class="form-group row">
            <label for="student_last_name" 
                   class="col-sm-3 col-form-label-lg">Last Name</label>
            <div class="col-sm-8">
              <input type="text" class="form-control" id="student_last_name" 
                     name="student_last_name" value= '<?=$student_last_name?>'
                     placeholder="Last Name" required>
              <div class="invalid-feedback">
                Please provide a valid last name.
              </div>
            </div>
          </div>
          <div class="form-group row">
            <label for="student_email" 
                   class="col-sm-3 col-form-label-lg">Email</label>
            <div class="col-sm-8">
              <input type="email" class="form-control" 
                     id="student_email" 
                     name="student_email" value= '<?=$student_email?>'
                     placeholder="Email" required>
              <div class="invalid-feedback">
                Please provide a valid email address.
              </div>
            </div>
          </div>
          <div class="form-group row">
            <label for="student_image_file" class="col-sm-3 col-form-label-lg">
                    Student Image File</label>
            <div class="col-sm-8">
              <input type="file" class="form-control-file"
                      id="student_image_file" name="student_image_file">
            </div>
          </div>
          <button class="btn btn-primary" type="submit" 
                  name="add_student_submission">Add Student</button>
        </form>
        <script>
        // JavaScript for disabling form submissions if there are invalid fields
        (function() {
          'use strict';
          window.addEventListener('load', function() {
            // Fetch all the forms we want to apply custom Bootstrap validation styles to
            var forms = document.getElementsByClassName('needs-validation');
            // Loop over them and prevent submission
            var validation = Array.prototype.filter.call(forms, function(form) {
              form.addEventListener('submit', function(event) {
                if (form.checkValidity() === false) {
                  event.preventDefault();
                  event.stopPropagation();
                }
                form.classList.add('was-validated');
              }, false);
            });
          }, false);
        })();
        </script>
        <?php
            } // Display add student form
        ?>
      </div>
    </div>
    <script src="https://code.jquery.com/jquery-3.3.1.slim.min.js"
            integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo"
            crossorigin="anonymous"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.6/umd/popper.min.js"
            integrity="sha384-wHAiFfRlMFy6i5SRaxvfOCifBUQy1xHdJ/yoi7FRNXMRBu5WHdZYu1hA6ZOblgut"
            crossorigin="anonymous"></script>
    <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.2.1/js/bootstrap.min.js"
            integrity="sha384-B0UglyR+jN6CkvvICOB2joaf5I4l3gm9GU6Hc1og6Ls7i6U/mkkaduKaBhlAXv9k"
            crossorigin="anonymous"></script>
  </body>
</html>