WPMake

← Back to BlogBulk-Importing Avatars for 500 Users With WP-CLITUTORIAL

Bulk-Importing Avatars for 500 Users With WP-CLI

⏱ 4 min read  ·  👤 iamprazol  ·  Sep 3, 2026

You are moving a membership site onto WordPress and you have a folder of five hundred profile photos named after email addresses. Setting those by hand is a week nobody has. Here is the script.

The shape of the job

Three steps per user, and only the middle one is specific to avatars:

  1. Find the WordPress user the file belongs to.
  2. Put the file in the media library, which gives you an attachment ID.
  3. Attach that ID to the user with wpmake_aua_set_user_avatar().

The script

Save this as import-avatars.php and run it with wp eval-file import-avatars.php. It expects a directory of images named for the user’s email address — maya@example.com.jpg, and so on.

<?php
require_once ABSPATH . 'wp-admin/includes/image.php';
require_once ABSPATH . 'wp-admin/includes/file.php';
require_once ABSPATH . 'wp-admin/includes/media.php';

$dir   = '/path/to/photos';
$files = glob( $dir . '/*.{jpg,jpeg,png,webp}', GLOB_BRACE );

$done = 0;
$skipped = 0;

foreach ( $files as $file ) {
    $email = pathinfo( $file, PATHINFO_FILENAME );
    $user  = get_user_by( 'email', $email );

    if ( ! $user ) {
        WP_CLI::warning( 'No user for ' . $email );
        continue;
    }

    // Idempotence: never re-upload for a user who already has a photo.
    if ( get_user_meta( $user->ID, 'wpmake_advance_user_avatar_attachment_id', true ) ) {
        ++$skipped;
        continue;
    }

    $tmp = wp_tempnam( basename( $file ) );
    copy( $file, $tmp );

    $attachment_id = media_handle_sideload(
        array(
            'name'     => basename( $file ),
            'tmp_name' => $tmp,
        ),
        0,
        'Avatar for ' . $user->display_name
    );

    if ( is_wp_error( $attachment_id ) ) {
        WP_CLI::warning( $email . ': ' . $attachment_id->get_error_message() );
        continue;
    }

    if ( wpmake_aua_set_user_avatar( $user->ID, $attachment_id ) ) {
        ++$done;
    }
}

WP_CLI::success( $done . ' avatars set, ' . $skipped . ' already had one.' );

Why it is written that way

The skip check comes before the upload. Re-running a script that sideloads first would fill your media library with duplicates on every run. Checking the user meta first makes the whole thing idempotent, which matters because you will run it more than once.

No capability check. Under WP-CLI there is no current user, so current_user_can() would be false for everything. The plugin’s setter deliberately does not call it — that is what makes this script possible. In a browser-facing handler you would call wpmake_aua_current_user_can_edit_avatar() yourself first.

The file is copied to a temp path. media_handle_sideload() moves the file it is given. Handing it your source file directly empties your source directory as it goes, which is a memorable way to learn this.

Sizes and cropping

An imported photo is not resized to your configured avatar size and not cropped square — those are functions of the front-end uploader, not of the setter. Whatever you sideload is what users get.

So crop the source files square before the import, with ImageMagick or whatever your export produced them from. A batch of magick mogrify -resize 500x500^ -gravity center -extent 500x500 beforehand will save you a lot of oddly-cropped faces afterwards.

Running it on five hundred users

  • Test on ten first. Point the script at a directory with ten files, check the results in the Users list, then run the rest.
  • Expect it to be slow. The time goes on image processing, not the database — every sideload generates the theme’s registered sizes plus the plugin’s 32, 64 and 96 pixel avatar copies. Five hundred users is minutes, not seconds.
  • Watch memory. Large source images and a low PHP memory limit is the usual failure. Crop first and the problem mostly goes away.
  • Log the misses. The users with no matching file are the interesting output. Redirect the warnings to a file and hand that list to whoever owns the data.

Then check your work in the admin. Users → Users Avatar → Manage Avatars lists every user with their current photo, which is the fastest way to see who the import missed.

The Manage Avatars table with Change and Remove buttons on each row
After the import, this list tells you what it did and did not cover.

Hooking into it

Every successful call fires wpmake_aua_avatar_set with the user ID and the attachment ID. During a bulk import that is a convenient place to write your own migration log, or to flag accounts for a follow-up email letting people know their photo came across.

Bulk-Importing Avatars for 500 Users With WP-CLI