Skip to content
Home » Blog » Computer Science » Web Development » WordPress » Plugins » How to Save a File to the WordPress Media Library using Contact Form 7 Upload Field

How to Save a File to the WordPress Media Library using Contact Form 7 Upload Field

4 minute read

By default, Contact Form 7 can only pass uploaded files as an attachment in an email. If you want to keep that image anywhere, particularly as an image that can be used by authors, editors, etc. in WordPress through its media library, then you’ll need to implement custom code like this to make it happen.

Start off with creating your form in Contact Form 7. Create a file form tag (you can specify filetypes and maximum file size using the filetypes and limit attributes).

For example, let’s create a required form tag with the name “logo”, limit uploads to just one (1) megabyte, and only accept PNG and JPG/JPEG images. That form tag might look like this in the form editor:

[file* logo limit:1mb filetypes:png|jpg|jpeg]

Now that we have a form field, here comes the code! Click here to skip to the full code example, otherwise keep reading for the full explanation!

Hook into wpcf7_before_send_mail

We want to add an action to the wpcf7_before_send_mail hook that accepts three (3) parameters. The first parameter is the current WPCF7_ContactForm object, the second parameter is a boolean for aborting the submission, and the third parameter is the WPCF7_Submission object. Basically, we want to hook into the form submission, snatch up the image the user uploaded, and then pull it into our WordPress media library. The shell of this function will look like this:

/**
 * Upload Logo on Form Submission
 *
 * @param WPCF7_ContactForm $contact_form The current contact form.
 * @param bool $abort If the submission is being aborted.
 * @param WPCF7_Submission $submission The current submission data.
 *
 * return void
 */
function au_cf7_upload_logo($contact_form, &$abort, $submission)
{
    // Do stuff
}
add_action('wpcf7_before_send_mail', 'au_cf7_upload_logo', 20, 3);

Inside this function, there are two variables we need to use. The first one is easy, it’s simply $_FILES while the other needs to be grabbed from the $submission object, so let’s start with the code and access those variables and simply check if the user uploaded their logo file.

if(array_key_exists('logo', $_FILES) && is_array($_FILES['logo'])) {
    $uploaded_files = $submission->uploaded_files();
}

Step 1: Access the Downloaded Image from Contact Form 7

Contact Form 7 already takes care of this, so we just need to access it. Let’s start by creating our $file array.

$file = array(
    'name' => $_FILES['logo']['name'], // basename with extension
    'type' => $_FILES['logo']['type'], // mime type
    'tmp_name' => $uploaded_files['logo'][0], // Full file path
    'error' => $_FILES['logo']['error'], // errors (hopefully 0)
    'size' => $_FILES['logo']['size'] // file size, in bytes
);

Why can’t we use the straight $_FILES['logo'] object for this? Well, because that array looks more like this:

[logo] => Array(
    [name] => example-file.jpg
    [full_path] => example-file.jpg
    [type] => image/jpeg
    [tmp_name] => /tmp/some-random-string
    [error] => 0
    [size] => 12345
)

It includes a full_path key/value pair but isn’t actually the real full path. The tmp_name is also unhelpful to us. If you do try to use this, you’ll likely see the error, “Specified file failed upload test.” and searching for that error will take you down all the wrong rabbit holes.

What we need in order to access the image is the real full path to the one Contact Form 7 temporarily downloaded. To find that, it’s in the $uploaded_files['logo'] array, which has just one item at the 0 index, and that is the full path we need. With the $file object ready, we’re good for the next step!

Step 2: Prepare the Attachment Data (optional)

You can skip this step unless you want to explicitly set information like the image title, description, caption, and assign a user ID as the one who uploaded it. That array would be something like this:

$attachment = array(
    'post_title' => '', // Image title
    'post_content' => '', // Image description
    'post_excerpt' => '', // Image caption
    'post_author' => 1, // WordPress user ID of person uploading image
);

Step 3: Upload the Image to the WordPress Media Library

We are going to use the magical WordPress function media_handle_sideload() for this job! Because we’re using this function on the frontend, the function doesn’t actually exist, so we need to call these babies first:

if (!function_exists('media_handle_sideload')) {
    require_once(ABSPATH . 'wp-admin/includes/media.php');
    require_once(ABSPATH . 'wp-admin/includes/file.php');
    require_once(ABSPATH . 'wp-admin/includes/image.php');
}

And now for the magic:

$attachment_id = media_handle_sideload(
    $file, // Uploaded image data
    0, // Reference a post ID
    $attachment['post_title'], // Set image title
    $attachment // Attributes
);

Only the first argument $file is required, the other three are optional. If you’re uploading this image to be associated with a specific post, you can pass the post ID as the second argument.

It’s also always good to add error handling. The media_handle_sideload() function returns the attachment ID on success or a WordPress error, so we can do a quick check for it and log it like this:

if (is_wp_error($attachment_id)) {
    error_log('Failed to upload ' . $file['name']);
    error_log($attachment_id->get_error_message());

}

Bonus: add an alt attribute!

If you’re looking to also automatically add the alt attribute, all you need is that $attachment_id (assuming the upload was successful). The text for the alt attribute is stored in the attachment’s metadata, so that is easy to update with this code:

update_post_meta(
    $attachment_id,
    '_wp_attachment_image_alt',
    'This is my alt text'
);

Optionally check for image before uploading it

This isn’t a perfect solution, but maybe you’d like to double check that the image isn’t already uploaded. Maybe we’ve already got example.jpg uploaded and we don’t want to upload another one. Though to be fair, WordPress would’ve uploaded it as example-2.jpg to sort out files with the same name, but maybe you don’t want that! In that case, I have this little function that searches by the filename and returns the attachment ID on success, 0 otherwise.

/**
 * Search WordPress Media by URL
 *
 * @param string $remote_url The file path or URL of the media to locate.
 *
 * @return int The attachment ID if found, 0 otherwise.
 */
function au_find_media_by_filename($remote_url)
{
    $remote_url = trim(sanitize_url($remote_url));
    if (empty($remote_url)) {
        return 0;
    }

    // Get the file extension
    $extension = sanitize_key(pathinfo($remote_url, PATHINFO_EXTENSION));

    // Get the absolute file URL
    $name = basename($remote_url, '.' . $extension); // Basename without extension

    // Search to see if this file already exists
    /** @var wpdb $wpdb WordPress Database */
    global $wpdb;
    $results = $wpdb->get_results($wpdb->prepare(
        "SELECT *
            FROM {$wpdb->prefix}posts
            WHERE SUBSTRING_INDEX(SUBSTRING_INDEX(guid, '/', -1), '.', 1) LIKE %s
            AND guid LIKE %s ORDER BY ID DESC",
        $name . '%',
        '%' . $name . '%.' . $extension
    ), ARRAY_A);
    if (count($results) > 0) {
        return $results[0]['ID']; // Return the ID of the first result
    }
    return 0;
}

Now you can go back to step 1 and create an if-statement to use this function first, and if an image wasn’t found, continue with uploading it like this:

$attachment_id = au_find_media_by_filename($_FILES['logo']['name']);
if(!$attachment_id) {
    // Continue with uploading the image
}

Full Code Snippet

Want something to just copy and paste all at once? Here you go!

/**
 * Search WordPress Media by URL
 *
 * @param string $remote_url The file path or URL of the media to locate.
 *
 * @return int The attachment ID if found, 0 otherwise.
 */
function au_find_media_by_filename($remote_url)
{
    $remote_url = trim(sanitize_url($remote_url));
    if (empty($remote_url)) {
        return 0;
    }

    // Get the file extension
    $extension = sanitize_key(pathinfo($remote_url, PATHINFO_EXTENSION));

    // Get the absolute file URL
    $name = basename($remote_url, '.' . $extension); // Basename without extension

    // Search to see if this file already exists
    /** @var wpdb $wpdb WordPress Database */
    global $wpdb;
    $results = $wpdb->get_results($wpdb->prepare(
        "SELECT *
            FROM {$wpdb->prefix}posts
            WHERE SUBSTRING_INDEX(SUBSTRING_INDEX(guid, '/', -1), '.', 1) LIKE %s
            AND guid LIKE %s ORDER BY ID DESC",
        $name . '%',
        '%' . $name . '%.' . $extension
    ), ARRAY_A);
    if (count($results) > 0) {
        return $results[0]['ID']; // Return the ID of the first result
    }
    return 0;
}

/**
 * Upload Logo on Form Submission
 *
 * @param WPCF7_ContactForm $contact_form The current contact form.
 * @param bool $abort If the submission is being aborted.
 * @param WPCF7_Submission $submission The current submission data.
 *
 * return void
 */
function au_cf7_upload_logo($contact_form, &$abort, $submission)
{
    // Skip if file already exists
    $attachment_id = au_find_media_by_filename($_FILES['logo']['name']);
    if(!$attachment_id) {
        // Continue with uploading the image
        $file = array(
            'name' => $_FILES['logo']['name'], // basename with extension
            'type' => $_FILES['logo']['type'], // mime type
            'tmp_name' => $uploaded_files['logo'][0], // Full file path
            'error' => $_FILES['logo']['error'], // errors (hopefully 0)
            'size' => $_FILES['logo']['size'] // file size, in bytes
        );
        $attachment = array(
            'post_title' => '', // Image title
            'post_content' => '', // Image description
            'post_excerpt' => '', // Image caption
            'post_author' => 1 // WordPress user ID of person uploading image
        );
        if (!function_exists('media_handle_sideload')) {
            require_once(ABSPATH . 'wp-admin/includes/media.php');
            require_once(ABSPATH . 'wp-admin/includes/file.php');
            require_once(ABSPATH . 'wp-admin/includes/image.php');
        }
        $attachment_id = media_handle_sideload(
            $file, // Uploaded image data
            0, // Reference a post ID
            $attachment['post_title'], // Set image title
            $attachment // Attributes
        );
        if (is_wp_error($attachment_id)) {
            error_log('Failed to upload ' . $file['name']);
            error_log($attachment_id->get_error_message());
        } else {
            update_post_meta(
                $attachment_id,
                '_wp_attachment_image_alt',
                'This is my alt text' // Set image alt attribute
            );
        }
    }
}
add_action('wpcf7_before_send_mail', 'au_cf7_upload_logo', 20, 3);

Related Post Module Attributes Before

array(29) {
  ["post_type"]=>
  bool(false)
  ["post_id"]=>
  string(5) "15614"
  ["exclude"]=>
  string(2) "on"
  ["title"]=>
  string(27) "You might also like…"
  ["description"]=>
  string(0) ""
  ["max"]=>
  string(1) "4"
  ["post_ids"]=>
  string(0) ""
  ["exclude_ids"]=>
  string(0) ""
  ["is_series"]=>
  string(0) ""
  ["featured_term"]=>
  string(0) ""
  ["exclude_terms"]=>
  string(0) ""
  ["exclusive"]=>
  string(0) ""
  ["order"]=>
  string(4) "DESC"
  ["show_image"]=>
  string(2) "on"
  ["image_size"]=>
  string(6) "medium"
  ["menu_order_label"]=>
  string(0) ""
  ["show_order_label"]=>
  string(2) "on"
  ["show_date"]=>
  string(2) "on"
  ["show_meta_keys"]=>
  string(2) "on"
  ["show_modified"]=>
  string(0) ""
  ["show_author"]=>
  string(0) ""
  ["show_categories"]=>
  string(0) ""
  ["show_primary_category"]=>
  string(0) ""
  ["show_description"]=>
  string(0) ""
  ["show_reading_time"]=>
  string(2) "on"
  ["show_cta"]=>
  string(2) "on"
  ["cta"]=>
  string(9) "Read more"
  ["autoplay"]=>
  string(0) ""
  ["allow_sticky"]=>
  string(0) ""
}

Related Post Module Attributes

array(29) {
  ["post_type"]=>
  bool(false)
  ["post_id"]=>
  string(5) "15614"
  ["exclude"]=>
  string(2) "on"
  ["title"]=>
  string(27) "You might also like…"
  ["description"]=>
  string(0) ""
  ["max"]=>
  string(1) "4"
  ["post_ids"]=>
  string(0) ""
  ["exclude_ids"]=>
  string(0) ""
  ["is_series"]=>
  string(0) ""
  ["featured_term"]=>
  string(0) ""
  ["exclude_terms"]=>
  string(0) ""
  ["exclusive"]=>
  string(0) ""
  ["order"]=>
  string(4) "DESC"
  ["show_image"]=>
  string(2) "on"
  ["image_size"]=>
  string(6) "medium"
  ["menu_order_label"]=>
  string(0) ""
  ["show_order_label"]=>
  string(2) "on"
  ["show_date"]=>
  string(2) "on"
  ["show_meta_keys"]=>
  string(2) "on"
  ["show_modified"]=>
  string(0) ""
  ["show_author"]=>
  string(0) ""
  ["show_categories"]=>
  string(0) ""
  ["show_primary_category"]=>
  string(0) ""
  ["show_description"]=>
  string(0) ""
  ["show_reading_time"]=>
  string(2) "on"
  ["show_cta"]=>
  string(2) "on"
  ["cta"]=>
  string(9) "Read more"
  ["autoplay"]=>
  string(0) ""
  ["allow_sticky"]=>
  string(0) ""
}

Nobody has commented on this yet, be the first!

Your email address will not be published. Required fields are marked *