135

How can I open files in a zip archive without extracting them first?

I'm using pygame. To save disk space, I have all the images zipped up. Is it possible to load a given image directly from the zip file? For example: pygame.image.load('zipFile/img_01')

d-cubed
  • 1,034
  • 5
  • 30
  • 58
user2880847
  • 1,353
  • 2
  • 9
  • 4

4 Answers4

170

Vincent Povirk's answer won't work completely;

import zipfile
archive = zipfile.ZipFile('images.zip', 'r')
imgfile = archive.open('img_01.png')
...

You have to change it in:

import zipfile
archive = zipfile.ZipFile('images.zip', 'r')
imgdata = archive.read('img_01.png')
...

For details read the ZipFile docs here.

phoenix
  • 7,988
  • 6
  • 39
  • 45
Jellema
  • 1,912
  • 1
  • 12
  • 15
  • image = pygame.image.load(imgfile, 'img_01.png') TypeError: must be string without null bytes, not str – user2880847 Oct 15 '13 at 02:15
  • 3
    Please explain your change. read returns a string with the file's contents; open returns a file-like object. pygame's documentation says image.load wants a filename or file-like object. – Esme Povirk Oct 17 '13 at 00:23
  • @Vincent Povirk: Thanks for commenting. The problem remains that image.load does accept a file-like object, but doesn't accept the zip-file-object. You have to adapt to that somehow. I'm not convinced of my answer either, it's not elegant yet... – Jellema Oct 17 '13 at 01:49
  • I agree with @EsmePovirk. The cited documentation seems to suggest that both `.read()` and `.open()` do not create temp files and provide `bytes` streams. The difference seems to be that `.open()` can be used as a context manager and supports seeking the reading position, while `.read()` reads the whole file. – Dr_Zaszuś Jan 17 '23 at 14:08
32
import io, pygame, zipfile
archive = zipfile.ZipFile('images.zip', 'r')

# read bytes from archive
img_data = archive.read('img_01.png')

# create a pygame-compatible file-like object from the bytes
bytes_io = io.BytesIO(img_data)

img = pygame.image.load(bytes_io)

I was trying to figure this out for myself just now and thought this might be useful for anyone who comes across this question in the future.

Brandon
  • 321
  • 3
  • 2
18

From Python 3.2 onwards it has been possible to use the ZipFile as a context manager:

from zipfile import ZipFile

with ZipFile('images.zip') as zf:
    for file in zf.namelist():
        if not file.endswith('.png'): # optional filtering by filetype
            continue
        with zf.open(file) as f:
            image = pygame.image.load(f, namehint=file)

  • The plus side of using context managers (with statement) is that the files are automatically closed properly.
  • The f can be used like regular file object you would get when using the built-in open().

Links to documentation

Niko Föhr
  • 28,336
  • 10
  • 93
  • 96
13

In theory, yes, it's just a matter of plugging things in. Zipfile can give you a file-like object for a file in a zip archive, and image.load will accept a file-like object. So something like this should work:

import zipfile
archive = zipfile.ZipFile('images.zip', 'r')
imgfile = archive.open('img_01.png')
try:
    image = pygame.image.load(imgfile, 'img_01.png')
finally:
    imgfile.close()
Esme Povirk
  • 3,004
  • 16
  • 24