Large Images Tool

Met deze gratis tool voeg je een extra menu-optie toe in je WordPress-dashboard:
Media → Large Images (of Grote afbeeldingen, afhankelijk van je taalinstellingen).

Op deze pagina zie je in één overzicht welke afbeeldingen in je mediabibliotheek de grootste zijn qua bestandsgrootte. Ideaal om snel opslagruimte vrij te maken en te beoordelen welke bestanden je eventueel kunt verkleinen, vervangen of verwijderen.

Zolang je de snippet geactiveerd laat via een plugin als WPCode of Code Snippets, blijft deze optie beschikbaar. Ben je klaar met opschonen, dan kun je de snippet eenvoudig weer deactiveren.

⚡ Wat doet deze tool?

  • Voegt een extra submenu toe onder Media → Large Images

  • Toont per afbeelding:

    • een kleine preview

    • bestandsnaam en upload-datum

    • afmetingen in pixels

    • bestandsgrootte in KB of MB

  • Sorteert de getoonde afbeeldingen binnen de huidige batch op bestandsgrootte, grootste eerst

  • Laat je meerdere afbeeldingen aanvinken en in één keer naar de prullenbak verplaatsen

  • Bevat een selecteer-alles vakje om snel een volledige batch te selecteren

⚠️ Waar moet je rekening mee houden?

  • Maak altijd eerst een backup van je site (bestanden en database) voordat je mediabestanden gaat verwijderen

  • Deze tool kijkt alleen naar bestandsgrootte, niet of een afbeelding nog ergens wordt gebruikt op de site
    Controleer dus zelf of een afbeelding nog in pagina’s, blogberichten, sliders, templates of e-mails wordt gebruikt voordat je hem verwijdert

  • Verwijderen gebeurt altijd via de WordPress-prullenbak voor media
    Staat er toch iets tussen dat je nog nodig hebt, dan kun je het achteraf vanuit de mediabibliotheek herstellen

  • Om je site en server soepel te houden:

    • per pagina wordt maar een beperkt aantal afbeeldingen opgehaald

    • per actie wordt maar een beperkt aantal geselecteerde afbeeldingen tegelijk naar de prullenbak verplaatst
      Dit verkleint de kans op timeouts en vastlopers, ook op grotere sites

⚡ Wanneer draait de code?

De zware logica van deze tool wordt alleen uitgevoerd wanneer jij (of een andere beheerder) de pagina Media → Large Images in het WordPress-dashboard opent.

Op de voorkant van je site (frontend) draait deze snippet niet.

Ook op andere admin-pagina’s, zoals Berichten, Pagina’s of Instellingen, wordt de eigen logica van de tool niet aangeroepen. Er wordt daar alleen een klein stukje code gebruikt om het menu-item te registreren, wat verwaarloosbaar licht is.

Dat betekent dat je bezoekers er niets van merken en dat je site niet trager wordt. Alleen wanneer jij bewust de Large Images-pagina opent, wordt er een batch afbeeldingen opgehaald, geanalyseerd en optioneel naar de prullenbak verplaatst.

📋 Installatie

  1. Kopieer de volledige PHP-code van de snippet

  2. Plaats de code in een snippet-plugin zoals WPCode of Code Snippets

  3. Stel de snippet in op Run Everywhere (of admin-only, als die optie beschikbaar is) en activeer hem

  4. In je WordPress-dashboard verschijnt nu een nieuwe optie Media → Large Images

  5. Open deze pagina, controleer de grootste afbeeldingen, vink aan wat je wilt opruimen en klik op Move selected to Trash om ze veilig naar de mediaprullenbak te verplaatsen

PHP code snippet:

				
					/**
 * Plugin Name: Large Images Inspector (Safe & Simple)
 * Description: Laat de grootste afbeeldingsbijlagen zien en verplaats geselecteerde items veilig naar de prullenbak.
 * Version: 1.0.0
 * Author: ChatGPT
 */

if ( ! defined( 'ABSPATH' ) ) {
    exit;
}

/**
 * Admin-pagina onder Media.
 */
add_action( 'admin_menu', function () {
    add_media_page(
        __( 'Large Images Inspector', 'large-images-inspector' ),
        __( 'Large Images', 'large-images-inspector' ),
        'manage_options',
        'large-images-inspector',
        'lii_render_admin_page'
    );
} );

/**
 * Render de adminpagina.
 */
function lii_render_admin_page() {
    if ( ! current_user_can( 'manage_options' ) ) {
        wp_die( esc_html__( 'You do not have permission to access this page.', 'large-images-inspector' ) );
    }

    $limit = (int) apply_filters( 'lii_scan_limit', 40 );
    $limit = $limit > 0 ? $limit : 40;

    $current_page = isset( $_GET['lii_page'] ) ? (int) $_GET['lii_page'] : 1;
    if ( $current_page < 1 ) {
        $current_page = 1;
    }

    $offset  = ( $current_page - 1 ) * $limit;
    $message = '';

    /**
     * Verwijder geselecteerde afbeeldingen (naar prullenbak).
     */
    if (
        isset( $_POST['lii_trash_selected'], $_POST['lii_media_ids'] )
        && is_array( $_POST['lii_media_ids'] )
        && ! empty( $_POST['lii_media_ids'] )
    ) {
        check_admin_referer( 'lii_trash_large_images' );

        $max_per_request = (int) apply_filters( 'lii_max_delete_per_request', 25 );
        $max_per_request = $max_per_request > 0 ? $max_per_request : 25;

        $ids_raw        = wp_unslash( $_POST['lii_media_ids'] );
        $total_selected = count( $ids_raw );
        $ids_raw        = array_slice( $ids_raw, 0, $max_per_request );

        $trashed = 0;

        if ( function_exists( 'set_time_limit' ) ) {
            @set_time_limit( 300 );
        }

        foreach ( $ids_raw as $raw_id ) {
            $attachment_id = (int) $raw_id;

            if ( $attachment_id <= 0 ) {
                continue;
            }

            if ( ! current_user_can( 'delete_post', $attachment_id ) ) {
                continue;
            }

            $attachment = get_post( $attachment_id );
            if ( ! $attachment || 'attachment' !== $attachment->post_type ) {
                continue;
            }

            $result = wp_delete_attachment( $attachment_id, false ); // naar prullenbak

            if ( $result ) {
                $trashed++;
            }
        }

        if ( $trashed ) {
            $message = sprintf(
                _n(
                    '%d attachment moved to the trash.',
                    '%d attachments moved to the trash.',
                    $trashed,
                    'large-images-inspector'
                ),
                $trashed
            );

            if ( $total_selected > $max_per_request ) {
                $message .= ' ' . sprintf(
                    esc_html__( '(Only the first %1$d of %2$d selected items were processed to keep things fast. Run again for the rest.)', 'large-images-inspector' ),
                    $max_per_request,
                    $total_selected
                );
            }
        } else {
            $message = esc_html__( 'No attachments were moved to the trash.', 'large-images-inspector' );
        }
    }

    // Haal een batch images op.
    $attachments = lii_get_image_attachments_batch( $limit, $offset );

    // Verrijk met file info en sorteer op bestandsgrootte (grootste eerst).
    $files = array();

    foreach ( $attachments as $attachment ) {
        $info = lii_describe_attachment_file( $attachment->ID );

        // Als er geen bruikbaar pad is, overslaan.
        if ( empty( $info['path'] ) || '-' === $info['size_bytes'] ) {
            continue;
        }

        $files[] = array(
            'id'         => $attachment->ID,
            'title'      => $attachment->post_title,
            'url'        => wp_get_attachment_url( $attachment->ID ),
            'path'       => $info['path'],
            'size'       => $info['size'],
            'size_bytes' => $info['size_bytes'],
            'dimensions' => $info['dimensions'],
            'date'       => $attachment->post_date,
        );
    }

    // Sorteer binnen deze batch op grootte.
    usort(
        $files,
        function ( $a, $b ) {
            if ( $a['size_bytes'] === $b['size_bytes'] ) {
                return 0;
            }
            return ( $a['size_bytes'] < $b['size_bytes'] ) ? 1 : -1; // grootste eerst
        }
    );

    echo '<div class="wrap">';
    echo '<h1>' . esc_html__( 'Large Images Inspector', 'large-images-inspector' ) . '</h1>';
    echo '<p>' . esc_html__( 'This tool lists your largest image attachments so you can review and clean them up safely. Selected files are moved to the media trash.', 'large-images-inspector' ) . '</p>';
    echo '<p class="description">' . esc_html(
        sprintf(
            __( 'Showing up to %1$d images from batch %2$d (ordered by upload date, then sorted by file size in this batch).', 'large-images-inspector' ),
            $limit,
            $current_page
        )
    ) . '</p>';

    if ( $message ) {
        echo '<div class="notice notice-success"><p>' . esc_html( $message ) . '</p></div>';
    }

    if ( empty( $files ) ) {
        echo '<p><strong>' . esc_html__( 'No images found in this batch.', 'large-images-inspector' ) . '</strong></p>';
        lii_render_pagination( $current_page, count( $attachments ), $limit );
        echo '</div>';
        return;
    }

    echo '<form method="post">';
    wp_nonce_field( 'lii_trash_large_images' );

    echo '<table class="widefat fixed striped">';
    echo '<thead><tr>';
    echo '<td class="manage-column column-cb check-column"><input type="checkbox" id="lii-select-all"></td>';
    echo '<th>' . esc_html__( 'Preview', 'large-images-inspector' ) . '</th>';
    echo '<th>' . esc_html__( 'File', 'large-images-inspector' ) . '</th>';
    echo '<th>' . esc_html__( 'Size', 'large-images-inspector' ) . '</th>';
    echo '<th>' . esc_html__( 'Dimensions', 'large-images-inspector' ) . '</th>';
    echo '<th>' . esc_html__( 'Uploaded', 'large-images-inspector' ) . '</th>';
    echo '</tr></thead>';
    echo '<tbody>';

    foreach ( $files as $file ) {
        $thumb = wp_get_attachment_image( $file['id'], array( 80, 80 ), true );
        echo '<tr>';
        echo '<th scope="row" class="check-column">';
        echo '<input type="checkbox" name="lii_media_ids[]" value="' . esc_attr( $file['id'] ) . '">';
        echo '</th>';

        echo '<td>' . ( $thumb ? $thumb : '' ) . '</td>';

        echo '<td>';
        echo '<strong><a href="' . esc_url( $file['url'] ) . '" target="_blank" rel="noopener noreferrer">' . esc_html( $file['title'] ) . '</a></strong><br>';
        echo '<span class="description">' . esc_html( basename( $file['path'] ) ) . '</span>';
        echo '</td>';

        echo '<td>' . esc_html( $file['size'] ) . '</td>';
        echo '<td>' . esc_html( $file['dimensions'] ) . '</td>';
        echo '<td>' . esc_html( mysql2date( get_option( 'date_format' ) . ' ' . get_option( 'time_format' ), $file['date'] ) ) . '</td>';
        echo '</tr>';
    }

    echo '</tbody>';
    echo '</table>';

    echo '<p class="description">' . esc_html__( 'Selected images will be moved to the media trash. You can restore them later from the Media Library if needed.', 'large-images-inspector' ) . '</p>';
    echo '<p><input type="submit" name="lii_trash_selected" class="button button-primary" value="' . esc_attr__( 'Move selected to Trash', 'large-images-inspector' ) . '"></p>';
    echo '</form>';

    lii_render_pagination( $current_page, count( $attachments ), $limit );

    echo '<script>';
    echo 'document.addEventListener("DOMContentLoaded", function() {';
    echo '  const selectAll = document.getElementById("lii-select-all");';
    echo '  if (selectAll) {';
    echo '    selectAll.addEventListener("change", function(event) {';
    echo '      const checkboxes = document.querySelectorAll("input[name=\'lii_media_ids[]\']");';
    echo '      checkboxes.forEach(function(checkbox) {';
    echo '        checkbox.checked = event.target.checked;';
    echo '      });';
    echo '    });';
    echo '  }';
    echo '});';
    echo '</script>';

    echo '</div>';
}

/**
 * Paginering (vorige / volgende batch).
 */
function lii_render_pagination( $current_page, $attachments_count, $limit ) {
    $current_page      = (int) $current_page;
    $attachments_count = (int) $attachments_count;
    $limit             = (int) $limit;

    $has_prev = $current_page > 1;
    $has_next = $attachments_count === $limit;

    if ( ! $has_prev && ! $has_next ) {
        return;
    }

    echo '<div class="tablenav"><div class="tablenav-pages">';

    $base_url = admin_url( 'upload.php?page=large-images-inspector' );

    if ( $has_prev ) {
        $prev_url = esc_url( add_query_arg( 'lii_page', $current_page - 1, $base_url ) );
        echo '<a class="button" href="' . $prev_url . '">&laquo; ' . esc_html__( 'Newer uploads', 'large-images-inspector' ) . '</a> ';
    }

    if ( $has_next ) {
        $next_url = esc_url( add_query_arg( 'lii_page', $current_page + 1, $base_url ) );
        echo '<a class="button" href="' . $next_url . '">' . esc_html__( 'Older uploads', 'large-images-inspector' ) . ' &raquo;</a>';
    }

    echo '</div></div>';
}

/**
 * Batch met image attachments ophalen.
 */
function lii_get_image_attachments_batch( $limit, $offset = 0 ) {
    $query = array(
        'post_type'      => 'attachment',
        'post_status'    => 'inherit',
        'post_mime_type' => 'image',
        'posts_per_page' => $limit,
        'orderby'        => 'date',
        'order'          => 'DESC',
        'fields'         => 'all',
        'offset'         => $offset,
    );

    return get_posts( $query );
}

/**
 * Bestandsinfo voor één attachment.
 */
function lii_describe_attachment_file( $attachment_id ) {
    $file_path  = get_attached_file( $attachment_id );
    $size       = '-';
    $size_bytes = '-';
    $dim        = '-';

    if ( $file_path && file_exists( $file_path ) ) {
        $bytes = filesize( $file_path );
        if ( false !== $bytes ) {
            $size       = size_format( $bytes, 2 );
            $size_bytes = (int) $bytes;
        }

        $image_info = @getimagesize( $file_path );
        if ( $image_info && isset( $image_info[0], $image_info[1] ) ) {
            $dim = $image_info[0] . '×' . $image_info[1];
        }
    }

    return array(
        'path'       => $file_path,
        'size'       => $size,
        'size_bytes' => $size_bytes,
        'dimensions' => $dim,
    );
}