Supabase Storage Download Folder as ZIP: 2 Ways
Supabase Storage download folder as ZIP: an edge function that lists the folder, packs files with JSZip, and returns a signed URL — plus the browser-side alternative.
<!-- BRIEF SPEC (not published): Variant: QUICK-ANSWER | Hook: BENEFIT-FIRST (direct answer frames the payoff: one signed URL, works for private buckets) | FAQ: 2 | Cover: LIGHT-MINIMAL (off-white, one bold geometric zip-box shape in a single accent color, lots of negative space) In-body images: (1) Flow diagram: list folder -> download files -> JSZip -> upload archive -> signed URL -> Flutter download. (Only one needed for this variant.) SERP structure notes (structure only): top results are one small blog with a Node/jszip handler and a fresh dev.to "5 ways" post — both code-first, thin on gotchas (memory limits, signed-URL expiry, plan limits). Gap we exploit: the stored-artifact + signed-URL pattern with real limits spelled out, plus the large-upload (TUS) half competitors ignore. Verified versions: Flutter 3.47.6 / Dart 3.13.5, @supabase/supabase-js 2.117.2, supabase_flutter 2.x stable. jszip unpinned (npm:jszip). --> Last updated: 2026-10-10 Supabase Storage has no built-in "download folder as ZIP." The reliable pattern: an edge function lists the folder, downloads each file, packs them with JSZip, uploads the archive to a temporary bucket, and returns a signed URL your Flutter app downloads. About 60 lines of TypeScript — and because the function uses the service-role key, it works for private buckets too. That last part is the payoff: one authenticated request, one download link, no RLS gymnastics on the client. Minimal working example: Supabase Storage download folder as ZIP Deploy this as zip-folder (verify-JWT can stay on — functions.invoke attaches the user's token automatically), and create a private exports bucket for the archives: // supabase/functions/zip-folder/index.ts import { createClient } from "jsr:@supabase/supabase-js@2.117.2"; import JSZip…