views:

6136

answers:

10

I want to create a zip file. Add a folder to the zip file and then add a bunch of files to that folder.

So I want to end up with a zip file with a single folder with files in.

I dont know if its bad practice to have folders in zip files or something but google gives me nothing on the subject.

I started out with this:

def addFolderToZip(myZipFile,folder):
    folder = folder.encode('ascii') #convert path to ascii for ZipFile Method
    for file in glob.glob(folder+"/*"):
            if os.path.isfile(file):
                print file
                myZipFile.write(file, os.path.basename(file), zipfile.ZIP_DEFLATED)
            elif os.path.isdir(file):
                addFolderToZip(myZipFile,file)

def createZipFile(filename,files,folders):
    curTime=strftime("__%Y_%m_%d", time.localtime())
    filename=filename+curTime;
    print filename
    zipFilename=utils.getFileName("files", filename+".zip")
    myZipFile = zipfile.ZipFile( zipFilename, "w" ) # Open the zip file for writing 
    for file in files:
        file = file.encode('ascii') #convert path to ascii for ZipFile Method
        if os.path.isfile(file):
            (filepath, filename) = os.path.split(file)
            myZipFile.write( file, filename, zipfile.ZIP_DEFLATED )

    for folder in  folders:   
        addFolderToZip(myZipFile,folder)  
    myZipFile.close()
    return (1,zipFilename)


(success,filename)=createZipFile(planName,files,folders);

Taken from: http://mail.python.org/pipermail/python-list/2006-August/396166.html

Which gets rid of all folders and puts all files in the target folder (and its subfolders) into a single zip file. I couldnt get it to add an entire folder.

If I feed the path to a folder in myZipFile.write, I get

IOError: [Errno 13] Permission denied: '..\packed\bin'

Any help is much welcome.

Related question: How do I zip the contents of a folder using python (version 2.5)?

+2  A: 

after adding some imports your code runs fine for me, how do you call the script, maybe you could tell us the folder structure of the '..\packed\bin' directory.

I called your code with the following arguments:

planName='test.zip'
files=['z.py',]
folders=['c:\\temp']
(success,filename)=createZipFile(planName,files,folders)

`

RSabet
Calling the code with those arguments would create a test.zip file containing both z.py and every file in c:\temp but no folders. Just a zip with many files.But I found an answer in the related question that seems to do what I want. Going to look more into that one.
mizipzor
Yes, maybe you could give an example how to call rhe code, so your error comes up.
RSabet
There is no error. The code creates a zip file and all that. The problem lies in that I want folders to be created when the file is unzipped, or the zip file containing folders depending on how you look at it.
mizipzor
thanks now i understand !
RSabet
+4  A: 

A zip file has no directory structure, it just has a bunch of pathnames and their contents. These pathnames should be relative to an imaginary root folder (the ZIP file itself). "../" prefixes have no defined meaning in a zip file.

Consider you have a file, a and you want to store it in a "folder" inside a zip file. All you have to do is prefix the filename with a folder name when storing the file in the zipfile:

zipi= zipfile.ZipInfo()
zipi.filename= "folder/a" # this is what you want
zipi.date_time= time.localtime(os.path.getmtime("a"))[:6]
zipi.compress_type= zipfile.ZIP_DEFLATED
filedata= open("a", "rb").read()

zipfile1.writestr(zipi, filedata) # zipfile1 is a zipfile.ZipFile instance

I don't know of any ZIP implementations allowing the inclusion of an empty folder in a ZIP file. I can think of a workaround (storing a dummy filename in the zip "folder" which should be ignored on extraction), but not portable across implementations.

ΤΖΩΤΖΙΟΥ
I see. So if I got this right, a zip file doesnt contain any folders. But if a file has a path separator in its name, its shown as a file within a folder in most archive managers? And will be created that way when the archive is unpacked?
mizipzor
Correct. It's hard to do, since the archive tools correctly escape path separators in file names. You python program, however, can force in unescaped names.
S.Lott
+9  A: 

Ok, after i understood what you want, it is as simple as using the second argument of zipfile.write, where you can use whatever you want:

import zipfile
myZipFile = zipfile.ZipFile("zip.zip", "w" )
myZipFile.write("test.py", "dir\\test.py", zipfile.ZIP_DEFLATED )

creates a zipfile where test.py would be extracted to a directory called dir

EDIT: I once had to create an empty directory in a zip file: it is possible. after the code above just delete the file test.py from the zipfile, the file is gone, but the empty directory stays.

RSabet
Yes, that looks like the thing I need. After reading ΤΖΩΤΖΙΟΥ's comment, I now understand how the "folders" in zip files work. And I found some code in the related question that also did what I want, but I didnt understand how. Ill experiment some more and return to post the code I actually used. :)
mizipzor
And what's best, it's the same way with `tarfile` as well, should you ever create one :) in `tarfile` the parameter is called `arcname` for archive name.
Matthieu M.
A: 

Heres the edited code I ran. Its based on the code above, taken from the mailing list. I added the imports and made a main routine. I also cut out the fiddling with the output filename to make the code shorter.

#!/usr/bin/env python

import os, zipfile, glob, sys

def addFolderToZip(myZipFile,folder):
    folder = folder.encode('ascii') #convert path to ascii for ZipFile Method
    for file in glob.glob(folder+"/*"):
            if os.path.isfile(file):
                print file
                myZipFile.write(file, os.path.basename(file), zipfile.ZIP_DEFLATED)
            elif os.path.isdir(file):
                addFolderToZip(myZipFile,file)

def createZipFile(filename,files,folders):
    myZipFile = zipfile.ZipFile( filename, "w" ) # Open the zip file for writing 
    for file in files:
        file = file.encode('ascii') #convert path to ascii for ZipFile Method
        if os.path.isfile(file):
            (filepath, filename) = os.path.split(file)
            myZipFile.write( file, filename, zipfile.ZIP_DEFLATED )

    for folder in  folders:   
        addFolderToZip(myZipFile,folder)  
    myZipFile.close()
    return (1,filename)

if __name__=="__main__":
    #put everything in sys.argv[1] in out.zip, skip files
    print createZipFile("out.zip", [], sys.argv[1])

At work, on my Windows box, this code ran fine but didnt create any "folders" in the zipfile. At least I recall it did. Now at home, on my Linux box, the zip file created seems to be bad:

$ unzip -l out.zip 
Archive:  out.zip
  End-of-central-directory signature not found.  Either this file is not
  a zipfile, or it constitutes one disk of a multi-part archive.  In the
  latter case the central directory and zipfile comment will be found on
  the last disk(s) of this archive.
unzip:  cannot find zipfile directory in one of out.zip or
        out.zip.zip, and cannot find out.zip.ZIP, period.

I dont know if I accidently broke the code, I think its the same. Crossplatform issues? Either way, its not related to my original question; getting folders in the zip file. Just wanted to post the code I actually ran, not the code I based my code on.

mizipzor
is this new code? Then it should be a new question. If not update your question with new facts. This doesn't appear to be an answer.
S.Lott
`sys.argv[1]` is a string. You pass it as a `folders`. When you iterate over a string you get a single letter at a time.
J.F. Sebastian
Post new questions as questions, not as answers.
J.F. Sebastian
+1  A: 
import zipfile
import os


class ZipUtilities:

    def toZip(self, file, filename):
     zip_file = zipfile.ZipFile(filename, 'w')
     if os.path.isfile(file):
              zip_file.write(file)
         else:
              self.addFolderToZip(zip_file, file)
     zip_file.close()

    def addFolderToZip(self, zip_file, folder): 
     for file in os.listdir(folder):
      full_path = os.path.join(folder, file)
      if os.path.isfile(full_path):
       print 'File added: ' + str(full_path)
       zip_file.write(full_path)
      elif os.path.isdir(full_path):
       print 'Entering folder: ' + str(full_path)
       self.addFolderToZip(zip_file, full_path)

def main():
    utilities = ZipUtilities()
    filename = 'TEMP.zip'
    directory = 'TEMP'
    utilities.toZip(directory, filename)

main()

I'm running:

python tozip.py

This is the log:

havok@fireshield:~$ python tozip.py

File added: TEMP/NARF (7ª copia)

Entering folder: TEMP/TEMP2

File added: TEMP/TEMP2/NERF (otra copia)

File added: TEMP/TEMP2/NERF (copia)

File added: TEMP/TEMP2/NARF

File added: TEMP/TEMP2/NARF (copia)

File added: TEMP/TEMP2/NARF (otra copia)

Entering folder: TEMP/TEMP2/TEMP3

File added: TEMP/TEMP2/TEMP3/DOCUMENTO DEL FINAL

File added: TEMP/TEMP2/TEMP3/DOCUMENTO DEL FINAL (copia)

File added: TEMP/TEMP2/NERF

File added: TEMP/NARF (copia) (otra copia)

File added: TEMP/NARF (copia) (copia)

File added: TEMP/NARF (6ª copia)

File added: TEMP/NERF (copia) (otra copia)

File added: TEMP/NERF (4ª copia)

File added: TEMP/NERF (otra copia)

File added: TEMP/NERF (3ª copia)

File added: TEMP/NERF (6ª copia)

File added: TEMP/NERF (copia)

File added: TEMP/NERF (5ª copia)

File added: TEMP/NARF (8ª copia)

File added: TEMP/NARF (3ª copia)

File added: TEMP/NARF (5ª copia)

File added: TEMP/NERF (copia) (3ª copia)

File added: TEMP/NARF

File added: TEMP/NERF (copia) (copia)

File added: TEMP/NERF (8ª copia)

File added: TEMP/NERF (7ª copia)

File added: TEMP/NARF (copia)

File added: TEMP/NARF (otra copia)

File added: TEMP/NARF (4ª copia)

File added: TEMP/NERF

File added: TEMP/NARF (copia) (3ª copia)

As you can see, it work, the archive is ok too. This is a recursive function that can zip an entire folder. The only problem is that it doesn't create a empty folder.

Cheers.

A: 

When you wanna create an empty folder, you can do it like this:

    storage = api.Storage.open("empty_folder.zip","w")
    res = storage.open_resource("hannu//","w")
    storage.close()

Folder not showe in winextractor, but when you extract it it is showed.

+2  A: 
A: 

here is my function i use to zip a folder:

import os
import os.path
import zipfile

def zip_dir(dirpath, zippath):
    fzip = zipfile.ZipFile(zippath, 'w', zipfile.ZIP_DEFLATED)
    basedir = os.path.dirname(dirpath) + '/' 
    for root, dirs, files in os.walk(dirpath):
        if os.path.basename(root)[0] == '.':
            continue #skip hidden directories        
        dirname = root.replace(basedir, '')
        for f in files:
            if f[-1] == '~' or (f[0] == '.' and f != '.htaccess'):
                #skip backup files and all hidden files except .htaccess
                continue
            fzip.write(root + '/' + f, dirname + '/' + f)
    fzip.close()
Dmitry Nedbaylo
A: 

Hi,

Thank you wery much for this useful function! I found it very useful as I was also searching for help. However, maybe it would be useful to change it a little bit so that

basedir = os.path.dirname(dirpath) + '/'

would be

basedir = os.path.dirname(dirpath + '/')

Because found that if I want to zip folder 'Example' which is located at 'C:\folder\path\notWanted\to\zip\Example',

I got in Windows:

dirpath = 'C:\folder\path\notWanted\to\zip\Example'
basedir = 'C:\folder\path\notWanted\to\zip\Example/'
dirname = 'C:\folder\path\notWanted\to\zip\Example\Example\Subfolder_etc'

But I suppose your code should give

dirpath = 'C:\folder\path\notWanted\to\zip\Example'
basedir = 'C:\folder\path\notWanted\to\zip\Example\'
dirname = '\Subfolder_etc'
note
A: 

None of you get it right, what is the original question about. I get it, as I just ran into the same problem, and I am looking for solution.

So: older zipper tools created archives with the following file list:

/directory/
/directory/file1.txt
/directory/subdir/
/directory/subdir/file2.exe

Now the unzipper tool has an easy task: if file ends with '/' it is a directory, and must be created.

Newer zips only create list of files like this:

/directory/file1.txt
/directory/subdir/file2.exe

Now. I need to create an older type of zip. And I can't do that in python. Has anyone solution for that?

kobor42