How to export the entire WordPress Media Library as a single ZIP file

Exporting / downloading your WordPress media library can be quick and hassle-free. This guide shows how to download all uploaded images as a single ZIP file without extra folders.

A setting for Export Media Library has been added into the Tools submenu as can be seen in this screenshot.

Export Media Library screen
Export Media Library screen

The following code can be added to any Code Snippet plugin as a PHP function.

// Export Media Library - Flat ZIP, originals only, auto-delete after download
add_action('admin_menu', function() {
    add_submenu_page(
        'tools.php',
        'Export Media Library',
        'Export Media Library',
        'manage_options',
        'export-media-library-flat',
        'beml_admin_page_flat'
    );
});

/**
 * Handle secure ZIP download before any output
 */
add_action('admin_init', function() {
    if (
        is_admin() &&
        isset($_GET['page']) &&
        $_GET['page'] === 'export-media-library-flat' &&
        isset($_GET['beml_download'])
    ) {
        $zipfile = get_option('beml_zipfile', '');
        if ($zipfile && file_exists($zipfile)) {

            // Clear any previous output
            while (ob_get_level()) {
                ob_end_clean();
            }

            // Force download headers
            nocache_headers();
            header('Content-Description: File Transfer');
            header('Content-Type: application/zip');
            header('Content-Disposition: attachment; filename="' . basename($zipfile) . '"');
            header('Content-Length: ' . filesize($zipfile));
            flush();

            // Send file
            readfile($zipfile);

            // Auto-delete after download
            @unlink($zipfile);
            delete_option('beml_zipfile');
            delete_option('beml_status');
            exit;
        }
    }
});

if (!function_exists('beml_admin_page_flat')) {
    /**
     * Render Export Media Library admin page
     */
    function beml_admin_page_flat() {
        // Current export status and ZIP path
        $status  = get_option('beml_status', '');
        $zipfile = get_option('beml_zipfile', '');

        // Handle export request
        if (isset($_POST['beml_start_export']) && check_admin_referer('beml_export', 'beml_export_nonce')) {

            global $wpdb;

            // Reuse recent ZIP if <10 minutes old
            if ($zipfile && file_exists($zipfile) && (time() - filemtime($zipfile) < 600)) {
                $status = 'completed';
            } else {
                // Delete old ZIP if it exists
                if (!empty($zipfile) && file_exists($zipfile)) {
                    @unlink($zipfile);
                    delete_option('beml_zipfile');
                }

                // Check ZipArchive availability
                if (!class_exists('ZipArchive')) {
                    $status = 'error_ziparchive';
                } else {
                    // Query all attachment IDs
                    $attachment_ids = $wpdb->get_col("
                        SELECT ID FROM $wpdb->posts 
                        WHERE post_type='attachment' 
                        AND post_status='inherit'
                    ");

                    if ($attachment_ids) {
                        $sitename   = sanitize_title(get_bloginfo('name'));
                        $zipname    = $sitename . '-media-library-' . date('Y-m-d-H-i-s') . '.zip';
                        $upload_dir = wp_upload_dir();
                        $zip_path   = $upload_dir['basedir'] . '/' . $zipname;

                        $zip = new ZipArchive();
                        if ($zip->open($zip_path, ZipArchive::CREATE | ZipArchive::OVERWRITE) === TRUE) {
                            // Add only original files
                            foreach ($attachment_ids as $att_id) {
                                $file = get_attached_file($att_id, true);
                                if ($file && file_exists($file)) {
                                    $zip->addFile($file, basename($file));
                                }
                            }
                            $zip->close();

                            update_option('beml_status', 'completed');
                            update_option('beml_zipfile', $zip_path);
                            $status  = 'completed';
                            $zipfile = $zip_path;
                        } else {
                            $status = 'error';
                        }
                    } else {
                        $status = 'empty';
                    }
                }
            }
        }

        ?>
        <div class="wrap">
            <h1>Export Media Library</h1>

            <?php if ($status === 'completed' && file_exists($zipfile)): ?>
                <p>Status: Export completed!</p>
                <a href="<?php echo esc_url(admin_url('tools.php?page=export-media-library-flat&beml_download=1')); ?>" 
                   class="button button-primary">Download ZIP</a>
                <form method="post" style="display:inline;">
                    <?php wp_nonce_field('beml_export', 'beml_export_nonce'); ?>
                    <input type="submit" name="beml_start_export" class="button" value="Start New Export">
                </form>

            <?php elseif ($status === 'empty'): ?>
                <p>No media found in the library.</p>
                <form method="post">
                    <?php wp_nonce_field('beml_export', 'beml_export_nonce'); ?>
                    <input type="submit" name="beml_start_export" class="button button-primary" value="Start Export">
                </form>

            <?php elseif ($status === 'error_ziparchive'): ?>
                <p>Error: PHP ZipArchive extension is required.</p>

            <?php elseif ($status === 'error'): ?>
                <p>Error creating ZIP.</p>
                <form method="post">
                    <?php wp_nonce_field('beml_export', 'beml_export_nonce'); ?>
                    <input type="submit" name="beml_start_export" class="button button-primary" value="Start Export">
                </form>

            <?php else: ?>
                <p>Click "Start Export" to begin exporting your media library.</p>
                <form method="post">
                    <?php wp_nonce_field('beml_export', 'beml_export_nonce'); ?>
                    <input type="submit" name="beml_start_export" class="button button-primary" value="Start Export">
                </form>
            <?php endif; ?>
        </div>
        <?php
    }
}

Another tutorial on import and export of WordPress web site.

Share the article:

Leave a Reply

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