Handling image uploads with custom validation using the Drupal 6 Forms API

I've recently had to go through the exercise again of building a bespoke form in Drupal 6 that accepts a file upload, validates it and saves it into to the server. Not doing this nearly enough to have it committed to memory means numerous trips to Google and frequent dives into the Drupal forms API are the order of the day. I've distilled my code into a working example that I hope will serve as future reference.

The sample code below assumes that you're comfortable creating a Drupal module, and have created a form using the forms API.

First we define a form with a single upload field and a submit button:

function module_image_form() {
  // Set the form's enctype to allow it to handle file uploads
  $form['#attributes']['enctype'] = "multipart/form-data";
  $form['logo'] = array(
    '#type' => 'file',
    '#title' => t('Logo'),
  $form['submit'] = array(
    '#type' => 'submit',
    '#value' => t('Save'),
  return $form;

Next we define the form's validate function which, after ensuring that the uploaded image meets our validation requirements, will handle the actual upload. Drupal provides built-in validation to check for a valid image, but we won't learn very much from using that. Instead, we specify our own validation function to prevent GIFs from being uploaded. We'll use Drupal's own mechanism for validating the image dimensions though.

function module_image_form_validate($form, &$form_state) {

  // Set this to the name of the form's file field
  $field = 'logo';

  // The directory the image will be saved to (file_directory_path() 
  // returns the path to your default files directory).
  $directory = file_directory_path() . '/images';

  // Drupal will attempt to resize the image if it is larger than 
  // the following maximum dimensions (width x height)
  $max_dimensions = '800x600';
  // We don't care about the minimum dimensions
  $min_dimensions = 0;

  // file_check_directory() ensures the destination directory is valid and 
  // will attempt to create it if it doesn't already exist.
  if (file_check_directory($directory, FILE_CREATE_DIRECTORY, $field)) {

    // Specify the validators. The first is Drupal's built-in function for validating
    // the image's dimensions. The second is our custom validator to exclude GIFs.
    $validators = array(
      'file_validate_image_resolution' => array($max_dimensions, $min_dimensions),
      'module_validate_image_type' => array(),

    // Move the file to its final location if the validators pass, else
    // return to the form and display the errors.
    if ($file = file_save_upload($field, $validators, $directory)) {

      // Set the file's status to permanent, which will prevent Drupal's file 
      // garbage collection from deleting it.
      file_set_status($file, FILE_STATUS_PERMANENT);

      // We add our final file object to the form's storage array, so that it gets passed
      // through to the form's submit handler, where we can act on it.
      $form_state['storage']['file'] = $file;


This is our custom validator, which simply checks to see that the uploaded image does not have a MIME type of image/gif:

function module_validate_image_type($file) {
  $errors = array();
  $info = image_get_info($file->filepath);
  if ($info['mime_type'] == 'image/gif') {
    $errors[] = t('Only JPEG and PNG images are allowed.');
  return $errors;

Finally, we implement the form's submit handler. The successfully uploaded image will be passed to it in the $form_state variable. I'll leave the submit handler blank, because what happens next depends entirely on what you're trying to achieve.

function module_sample_form_submit($form, &$form_state) {
  // print_r($form_state['storage']['file']) to view the uploaded file's details