{"id":349172,"date":"2026-08-10T04:24:18","date_gmt":"2026-08-10T04:24:18","guid":{"rendered":"https:\/\/wordpress.org\/plugins\/zealot-bulk-image-assign-by-sku-for-woocommerce\/"},"modified":"2026-08-10T04:41:27","modified_gmt":"2026-08-10T04:41:27","slug":"zealot-bulk-image-assign-by-sku-for-woocommerce","status":"publish","type":"plugin","link":"https:\/\/wordpress.org\/plugins\/zealot-bulk-image-assign-by-sku-for-woocommerce\/","author":23530849,"comment_status":"closed","ping_status":"closed","template":"","meta":{"version":"0.10.2","stable_tag":"0.10.2","tested":"7.0.3","requires":"6.2","requires_php":"7.4","requires_plugins":null,"header_name":"Zealot Bulk Image Assign by SKU for WooCommerce","header_author":"WPZealot","header_description":"Bulk-match media library images to WooCommerce products by SKU. Name files <sku>_main.jpg and <sku>_1.jpg (the Amazon convention), scan, preview, and assign featured + gallery images at any scale.","assets_banners_color":"19033b","last_updated":"2026-08-10 04:41:27","external_support_url":"","external_repository_url":"","donate_link":"","header_plugin_uri":"https:\/\/wpzealot.com\/docs\/product-images-by-sku\/","header_author_uri":"https:\/\/wpzealot.com","rating":0,"author_block_rating":0,"active_installs":0,"downloads":31,"num_ratings":0,"support_threads":0,"support_threads_resolved":0,"author_block_count":0,"sections":["description","changelog"],"tags":{"0.10.1":{"tag":"0.10.1","author":"wpzealot","date":"2026-08-10 04:23:57"},"0.10.2":{"tag":"0.10.2","author":"wpzealot","date":"2026-08-10 04:41:27"}},"upgrade_notice":[],"ratings":[],"assets_icons":{"icon-256x256.png":{"filename":"icon-256x256.png","revision":3639944,"resolution":"256x256","location":"assets","locale":"","width":256,"height":256}},"assets_banners":{"banner-1544x500.png":{"filename":"banner-1544x500.png","revision":3639950,"resolution":"1544x500","location":"assets","locale":"","width":1544,"height":500},"banner-772x250.png":{"filename":"banner-772x250.png","revision":3639950,"resolution":"772x250","location":"assets","locale":"","width":772,"height":250}},"assets_blueprints":{},"all_blocks":[],"tagged_versions":["0.10.1","0.10.2"],"block_files":[],"assets_screenshots":{"screenshot-1.png":{"filename":"screenshot-1.png","revision":3639944,"resolution":"1","location":"assets","locale":"","width":1115,"height":780},"screenshot-2.png":{"filename":"screenshot-2.png","revision":3639944,"resolution":"2","location":"assets","locale":"","width":1086,"height":896}},"screenshots":{"1":"The naming rules up front, so a scan does what you expect. Past runs are listed underneath and any of them can be undone in one click.","2":"Every proposed match shown with thumbnails before anything is written, split into ready to apply, already applied, and unmatched. Optional cloud resize, aspect and watermark sit below."}},"plugin_section":[],"plugin_tags":[3236,210,24716,9020,286],"plugin_category":[45,50],"plugin_contributors":[273408],"plugin_business_model":[],"class_list":["post-349172","plugin","type-plugin","status-publish","hentry","plugin_tags-bulk","plugin_tags-gallery","plugin_tags-product-images","plugin_tags-sku","plugin_tags-woocommerce","plugin_category-ecommerce","plugin_category-media","plugin_contributors-wpzealot","plugin_committers-wpzealot"],"banners":{"banner":"https:\/\/ps.w.org\/zealot-bulk-image-assign-by-sku-for-woocommerce\/assets\/banner-772x250.png?rev=3639950","banner_2x":"https:\/\/ps.w.org\/zealot-bulk-image-assign-by-sku-for-woocommerce\/assets\/banner-1544x500.png?rev=3639950","banner_rtl":false,"banner_2x_rtl":false},"icons":{"svg":false,"icon":"https:\/\/ps.w.org\/zealot-bulk-image-assign-by-sku-for-woocommerce\/assets\/icon-256x256.png?rev=3639944","icon_2x":"https:\/\/ps.w.org\/zealot-bulk-image-assign-by-sku-for-woocommerce\/assets\/icon-256x256.png?rev=3639944","generated":false},"screenshots":[{"src":"https:\/\/ps.w.org\/zealot-bulk-image-assign-by-sku-for-woocommerce\/assets\/screenshot-1.png?rev=3639944","caption":"The naming rules up front, so a scan does what you expect. Past runs are listed underneath and any of them can be undone in one click."},{"src":"https:\/\/ps.w.org\/zealot-bulk-image-assign-by-sku-for-woocommerce\/assets\/screenshot-2.png?rev=3639944","caption":"Every proposed match shown with thumbnails before anything is written, split into ready to apply, already applied, and unmatched. Optional cloud resize, aspect and watermark sit below."}],"raw_content":"<!--section=description-->\n<p>WooCommerce can set <strong>one featured image<\/strong>, for <strong>one product<\/strong>, at the <strong>moment you upload a file<\/strong> whose name is exactly the SKU. That is the whole feature, and if it is all you need, you do not need this plugin.<\/p>\n\n<p>This plugin is for the rest of it:<\/p>\n\n<ul>\n<li><strong>Full galleries, not one image.<\/strong> <code>&lt;sku&gt;_1.jpg<\/code>, <code>&lt;sku&gt;_2.jpg<\/code> become the product gallery, in order.<\/li>\n<li><strong>Variation images.<\/strong> Name a file after a variation's own SKU and it lands on that variation.<\/li>\n<li><strong>The library you already have.<\/strong> WooCommerce only matches at upload, so it can never touch the thousands of images already sitting in your media library. This scans all of them.<\/li>\n<li><strong>A preview before anything is written<\/strong>, and one-click undo afterwards.<\/li>\n<\/ul>\n\n<p>WooCommerce's own setting lives at Settings -&gt; Products -&gt; Advanced. If you leave it on, both will\nact on new uploads, and anything it assigns bypasses the preview, the undo and the optional cloud\nprocessing here. The plugin notices when it is on and offers to switch it off for you.<\/p>\n\n<p>It uses the same filename convention Amazon uses for bulk seller uploads:<\/p>\n\n<ul>\n<li><code>&lt;sku&gt;_main.jpg<\/code> -&gt; the product's featured (main) image<\/li>\n<li><code>&lt;sku&gt;_1.jpg<\/code>, <code>&lt;sku&gt;_2.jpg<\/code> -&gt; gallery images, in order<\/li>\n<li><code>&lt;sku&gt;.jpg<\/code> -&gt; featured image (simple fallback)<\/li>\n<\/ul>\n\n<p>Matching is case-sensitive: <code>abc123_main.jpg<\/code> will not match a product whose SKU is <code>ABC123<\/code>. Copy\nthe SKU exactly as it appears on the product. Only the SKU is strict, so <code>_main<\/code> and <code>_MAIN<\/code> mean the\nsame thing. A file that misses on case is listed under \"Unmatched images\" after a scan rather than\napplied to a guess, so you can spot it and rename it.<\/p>\n\n<h4>Product variations<\/h4>\n\n<p>Name the file after the VARIATION's own SKU, not the parent's: <code>&lt;variation_sku&gt;.jpg<\/code> sets that\nvariation's image. A variation displays a single image, so it has no gallery. If you name several\nfiles for one variation (<code>&lt;variation_sku&gt;_1.jpg<\/code>, <code>_2<\/code>, <code>_3<\/code>), the lowest number is used as the\nvariation's image and the rest are reported as conflicts rather than applied silently. Put extra\nshots on the parent product, whose gallery does show them.<\/p>\n\n<h4>What else it does<\/h4>\n\n<ul>\n<li><strong>Unlimited batches<\/strong>: no 10- or 20-image cap; large catalogs run in safe background batches.<\/li>\n<li><strong>Rename-proof matching<\/strong>: survives WordPress's own <code>-scaled<\/code>, <code>-768x1024<\/code>, and duplicate <code>-1<\/code> filename changes, and records each image's original upload name so later optimization (Imagify, ShortPixel, Smush, EWWW) never breaks a match.<\/li>\n<li><strong>Preview before applying<\/strong>: see every proposed match and deselect any before anything is written.<\/li>\n<li><strong>One-click rollback<\/strong>: revert a completed run.<\/li>\n<li><strong>Import straight from your supplier<\/strong>: have the WPZealot service pull images from a URL list, or from your supplier's SFTP folder (Pro).<\/li>\n<li><strong>Optional image resizing\/optimization<\/strong>: offload resize + compression to the WPZealot cloud (Pro).<\/li>\n<\/ul>\n\n<p>Matching runs entirely on your own server: filename-to-SKU is plain string work, so no images leave your site for the core feature.<\/p>\n\n<h4>Importing from SFTP (Pro)<\/h4>\n\n<p>Most suppliers deliver photography to an SFTP folder rather than a public link. Give the WPZealot service the folder details and it fetches new files for you; the plugin copies them into your media library with their filenames intact, so the usual SKU matching applies.<\/p>\n\n<p>The connection is made by the WPZealot cloud service, not by your store. Your username and password or key are stored by that service, encrypted, and are used only to reach the server you nominated. Your store never opens an SSH connection and never holds the credential. See \"External services\" below.<\/p>\n\n<p>The service records the server's host key the first time it connects and pins it. If that key ever changes, the import stops rather than handing your credential to a server that is not the one you set up.<\/p>\n\n<p>We recommend asking your supplier for key-based access and an account that can do nothing but read that one folder.<\/p>\n\n<h3>External services<\/h3>\n\n<p>The core matching and assignment features run locally and contact no external service.<\/p>\n\n<p>The OPTIONAL cloud features connect to the WPZealot cloud service\n(https:\/\/images-by-sku.wpzealot.com), operated by WPZealot, and are off by default.\nThe service is contacted only after you press Connect, and never before. When you enable it:<\/p>\n\n<ul>\n<li>Your site URL, WordPress\/WooCommerce\/PHP versions, and plan usage counts are sent for licensing and batch sizing.<\/li>\n<li>Image resizing: the image files you choose to optimize are uploaded to the service, processed, and returned to your store.<\/li>\n<li>URL import: the addresses you supply are fetched by the service, which returns the image data to your store.<\/li>\n<li>SFTP import: the address, username and password or key of the server YOU nominate are sent to the\nservice and stored there, encrypted, so that it can connect on your behalf and fetch new files.\nThey are used for nothing else, are never returned to your site or shared with any third party,\nand are deleted when you remove the server in the plugin or disconnect from the service.<\/li>\n<li>No customer or order data is ever read or transmitted.<\/li>\n<\/ul>\n\n<p>The service's terms are at https:\/\/wpzealot.com\/terms and its privacy policy at https:\/\/wpzealot.com\/privacy.<\/p>\n\n<h3>Source code and build<\/h3>\n\n<p>The admin screen is a React app. Its compiled bundle ships at <code>assets\/app\/wpz-pis.js<\/code> and\n    assets\/app\/wpz-pis.css because a WordPress install has no build step, and the complete,\nhuman-readable source that produced it ships alongside it in the <code>src\/<\/code> folder of this plugin.<\/p>\n\n<p>To rebuild it, from <code>src\/<\/code>:<\/p>\n\n<pre><code>npm install\nnpm run build\n<\/code><\/pre>\n\n<p>Vite writes the result back to <code>assets\/app\/<\/code>. See <code>src\/README.md<\/code> for detail. Every other file in\nthis plugin is source: nothing else is generated, minified or obfuscated.<\/p>\n\n<!--section=changelog-->\n<h4>0.10.2<\/h4>\n\n<ul>\n<li>SKU matching is case-sensitive, and now says so. The naming guide above the Scan button, this readme and the documentation all state the rule and show an example, so a filename whose letter case does not match is something you can see and fix rather than something that quietly matches nothing.<\/li>\n<\/ul>\n\n<h4>0.10.1<\/h4>\n\n<ul>\n<li>Fixed: SKU matching is now consistently case-sensitive. Two internal lookup paths disagreed with each other, so whether a filename whose letter case differed from the product's SKU matched at all depended on how the scan happened to run.<\/li>\n<li>Fixed: disconnecting from the WPZealot service no longer discards this site's cloud identity. Reconnecting used to register the site as a brand new one, which needed its own subscription while the original stayed active and unreachable from Manage billing. Disconnect now revokes the stored key only, so reconnecting returns you to the same account and the same plan.<\/li>\n<\/ul>\n\n<h4>0.10.0<\/h4>\n\n<ul>\n<li>Variation matching and one-click Undo are now free, for everyone. They run on your own server, so they are simply part of the plugin.<\/li>\n<li>SFTP import moved to the WPZealot service. The service now holds the credential and makes the connection; your store no longer opens SSH connections and no longer stores supplier passwords. See \"External services\" for exactly what is sent and kept.<\/li>\n<li>Removed the bundled phpseclib library along with the local SFTP client, which takes about 3 MB off the plugin.<\/li>\n<li>Scheduled auto-matching has been withdrawn while it moves to the service. It will return as a scheduled run driven by the cloud.<\/li>\n<li>The React source for the admin screen now ships in <code>src\/<\/code>, with build instructions.<\/li>\n<\/ul>\n\n<h4>0.9.0<\/h4>\n\n<ul>\n<li>Pro: import images from an SFTP folder. Connect the folder your supplier drops files into and the plugin copies new images into your media library with their filenames intact, so they match by SKU like any upload. Optionally checks the folder on a schedule, and hands off to auto-matching so a supplier drop becomes live product images unattended.<\/li>\n<li>The connection is made by your own site, so the credential never reaches the WPZealot cloud. It is stored encrypted, and removed when you delete the plugin.<\/li>\n<li>New servers must have their host key fingerprint confirmed before any credential is sent, and a changed fingerprint stops the import instead of trusting it.<\/li>\n<\/ul>\n\n<h4>0.8.0<\/h4>\n\n<ul>\n<li>Pro: scheduled and watch-folder auto-matching. Turn it on and images are assigned to matching products automatically: a background sweep runs shortly after new uploads and once daily, applying every changed, non-locked product (rollback-able, variation-aware). Gated behind the Pro plan.<\/li>\n<\/ul>\n\n<h4>0.7.0<\/h4>\n\n<ul>\n<li>Pro: variation-level matching. Filenames matching a product variation's SKU (e.g. _main.jpg) now set that variation's image. Variations are detected, shown with their attribute label under a \"Variations (Pro)\" tab, and gated behind the Pro plan (server-side).<\/li>\n<\/ul>\n\n<h4>0.6.0<\/h4>\n\n<ul>\n<li>Pro tier: plan\/feature model (free vs Pro) with server-side gating. Pro unlocks cloud resize, one-click Undo, variation-level matching, and scheduled auto-matching. Undo is now a Pro feature. Added an \"Upgrade to Pro\" button (Stripe Checkout) and \"Manage billing\" (Stripe Customer Portal), mirroring WPZealot Easy Import.<\/li>\n<\/ul>\n\n<h4>0.5.2<\/h4>\n\n<ul>\n<li>Added the <code>wpz_pis_resize_source_url<\/code> filter so sites whose media sits behind auth, a private host, or a local dev domain can hand the cloud a publicly-reachable image URL for optimization.<\/li>\n<\/ul>\n\n<h4>0.5.1<\/h4>\n\n<ul>\n<li>Resize: when the cloud converts the format (e.g. to WebP), the file is now rewritten with the correct extension and the attachment's file path, MIME type, and URL are updated so WordPress serves it correctly. Restore reverses the format change too.<\/li>\n<\/ul>\n\n<h4>0.5.0<\/h4>\n\n<ul>\n<li>Pro: cloud image resize\/optimization. A \"Resize &amp; optimize\" panel lets you set max dimension, quality, and format (WebP\/auto), then downscale + compress the matched product images through the WPZealot cloud (image-CDN backed). Originals are backed up and one-click restorable. Server-side plan gating. Shown only when the cloud is enabled and your plan includes resizing.<\/li>\n<\/ul>\n\n<h4>0.4.2<\/h4>\n\n<ul>\n<li>Renamed the \"Needs changes\" tab to \"Ready to apply\".<\/li>\n<\/ul>\n\n<h4>0.4.1<\/h4>\n\n<ul>\n<li>Preview now separates matches into \"Ready to apply\", \"Already applied\", and \"Unmatched images\", so products that already have the matched image aren't shown as pending work. Only changed products are selected by default; new images are highlighted and already-assigned ones are dimmed.<\/li>\n<\/ul>\n\n<h4>0.4.0<\/h4>\n\n<ul>\n<li>React admin app: scan the media library, preview every proposed match (featured + gallery thumbnails, per product), select\/deselect, apply in bulk, watch progress, and undo any run. Small runs apply instantly; large runs process in the background.<\/li>\n<\/ul>\n\n<h4>0.3.0<\/h4>\n\n<ul>\n<li>Apply engine (WooCommerce CRUD), background apply queue via Action Scheduler (unlimited batches), rollback store, media scanner, and the admin REST API (scan\/apply\/status\/rollback\/runs).<\/li>\n<\/ul>\n\n<h4>0.2.0<\/h4>\n\n<ul>\n<li>Source-filename capture at upload (<code>_wpz_pis_source_filename<\/code>) so matches survive later renaming\/optimization.<\/li>\n<li>Matcher: turns the media library into a per-product assignment plan, with contextual duplicate-suffix and separator-in-SKU fallback resolution, conflict detection, and unmatched reporting.<\/li>\n<\/ul>\n\n<h4>0.1.0<\/h4>\n\n<ul>\n<li>Initial scaffold: plugin foundation, shared WPZealot admin menu, and the filename normalization + SKU convention parser (the matching core), with unit tests.<\/li>\n<\/ul>","raw_excerpt":"Fill entire product galleries and variation images from your media library by SKU. Preview every match before anything is written, and undo it after.","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin\/349172","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin"}],"about":[{"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/types\/plugin"}],"replies":[{"embeddable":true,"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/comments?post=349172"}],"author":[{"embeddable":true,"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wporg\/v1\/users\/wpzealot"}],"wp:attachment":[{"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/media?parent=349172"}],"wp:term":[{"taxonomy":"plugin_section","embeddable":true,"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_section?post=349172"},{"taxonomy":"plugin_tags","embeddable":true,"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_tags?post=349172"},{"taxonomy":"plugin_category","embeddable":true,"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_category?post=349172"},{"taxonomy":"plugin_contributors","embeddable":true,"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_contributors?post=349172"},{"taxonomy":"plugin_business_model","embeddable":true,"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_business_model?post=349172"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}