Desk Clarity

Everyday document & browser help

Python ZIP Is Larger Than the Source: Check Members, Compression and Output Location

By ON Lab · Sources checked 8 November 2026

A ZIP file is a container, so its final size is not just the compressed payload. Tiny files can grow because entry records and names cost bytes; already-compressed content may barely shrink. A much larger or continuously growing archive also deserves a different check: did the output ZIP become one of the files being added?

Compare the same quantities first

Use file size in bytes rather than comparing a folder's allocated disk space with an archive's logical length. Count the intended source files, then inspect the member list. Extra old exports, duplicate members or an output file inside the input tree can change the amount of data being archived. A compression setting cannot correct the wrong input set.

The original public report used a directory walk while writing the destination archive. Its answer identified self-inclusion as a possibility. That is a useful diagnostic branch, not proof that every oversized ZIP has that cause. Do not reproduce an uncontrolled self-inclusion test on a real folder.

Inspect a trusted archive without extracting it

from pathlib import Path
import zipfile

archive = Path("bundle.zip")  # A small archive you created or trust.
with zipfile.ZipFile(archive) as z:
    entries = z.infolist()
    for item in entries:
        print(item.filename, item.file_size,
              item.compress_size, item.compress_type)
    print("entries", len(entries))
    print("original member bytes", sum(i.file_size for i in entries))
    print("compressed member bytes", sum(i.compress_size for i in entries))
print("whole archive bytes", archive.stat().st_size)

Method 0 is stored without compression; method 8 is DEFLATE. Compare each member's original and compressed size, then compare their compressed total with the whole archive. The remaining bytes include ZIP metadata. That gap is expected, and it depends on names, entry counts and format features; it is not a fixed percentage.

Opening an untrusted archive can still involve excessive metadata or resource use. Do not extract an unfamiliar archive merely to measure it. The examples here are for small controlled inputs, not an archive security scanner.

Reproduce normal growth with a tiny input

from io import BytesIO
import zipfile

for method in (zipfile.ZIP_STORED, zipfile.ZIP_DEFLATED):
    buffer = BytesIO()
    with zipfile.ZipFile(buffer, "w", compression=method) as z:
        z.writestr("tiny.txt", b"A")
    data = buffer.getvalue()
    with zipfile.ZipFile(BytesIO(data)) as z:
        info = z.getinfo("tiny.txt")
        assert z.read("tiny.txt") == b"A"
        print(info.compress_type, info.file_size,
              info.compress_size, len(data))

Our Python 3.13.18 run printed 0 1 1 115 and 8 1 3 117. Both archives preserved the one-byte content. The stored archive was larger than the original despite performing no compression at all. The DEFLATE member also grew slightly before container overhead was added.

A separate synthetic fixture compressed 65,000 bytes of repeated text to a 158-byte member inside a 276-byte archive. A 65,536-byte deterministic pseudo-random fixture instead produced a 65,556-byte DEFLATE member in a 65,672-byte archive. Those are observed test values, not promised sizes for your files or other compressor versions.

Keep a new output outside the source tree

This self-contained example makes a temporary source folder and writes a sibling archive. It uses exclusive creation so an existing destination raises an error, and verifies the expected member before the temporary directory is removed:

from pathlib import Path
from tempfile import TemporaryDirectory
import zipfile

with TemporaryDirectory() as temporary:
    parent = Path(temporary)
    source = parent / "input"
    source.mkdir()
    (source / "a.txt").write_bytes(b"hello\n")
    destination = parent / "bundle.zip"

    with zipfile.ZipFile(destination, "x",
                         compression=zipfile.ZIP_DEFLATED) as z:
        for file in sorted(source.rglob("*")):
            if file.is_file():
                z.write(file, arcname=file.relative_to(source))

    with zipfile.ZipFile(destination) as z:
        assert z.namelist() == ["a.txt"]
        assert z.read("a.txt") == b"hello\n"
        assert z.testzip() is None
        print(z.namelist())

The result was ['a.txt']. The destination could not enter the input traversal because it was a sibling of the input directory. When adapting this to real data, choose and inspect the source and output locations deliberately. Existing archive files already inside the source are still ordinary inputs to this loop. Remove them from the intended input selection explicitly if they should not be included.

This is a demonstration for a small, trusted tree, not a complete backup utility. Symbolic links, concurrently changing files, permissions, empty directories and recovery after a failed write need separate policies. Do not run it against your whole home directory. If archive member names are surprising, the archive root and stored member prefix guide addresses that separate issue.

Choose the next action from what you measured

If the correct member set compresses well but the outer file remains slightly larger than expected, account for container overhead. If individual members barely shrink, check whether they are already compressed, such as common image, media or document formats. Wrapping them in another ZIP is useful for bundling, but it does not guarantee a size reduction.

If the member list is wrong, fix the input selection before changing compression levels. If the output is growing during creation, stop your own archive job safely and inspect its destination relationship to the source. Do not delete original files to make a target size appear achievable. Test a new archive at a separate destination and retain the originals until content verification passes.

If a correctly scoped archive still exceeds an upload limit, changing a ZIP setting may not solve the constraint. Revisit the allowed submission format, select fewer intended inputs, or use an already authorized transfer method. No paid compression product is required for these diagnostics, and none can promise arbitrary lossless reduction for every input.

Method: We compared six in-memory stored/DEFLATE cases and one temporary-directory creation case on Python 3.13.18, checking member bytes after reopening. The fixture used tiny synthetic data, no user files and no self-including archive. We did not benchmark every compression tool, execute the historical poster's script, or verify an external upload service.

Sources