appypod-rattail/shared/zip.py

95 lines
4.5 KiB
Python

'''Functions for (un)zipping files'''
# ------------------------------------------------------------------------------
import os, os.path, zipfile, time
from appy.shared import mimeTypes
# ------------------------------------------------------------------------------
def unzip(f, folder, odf=False):
'''Unzips file p_f into p_folder. p_f can be any anything accepted by the
zipfile.ZipFile constructor. p_folder must exist.
If p_odf is True, p_f is considered to be an odt or ods file and this
function will return a dict containing the content of content.xml and
styles.xml from the zipped file.'''
zipFile = zipfile.ZipFile(f)
if odf: res = {}
else: res = None
for zippedFile in zipFile.namelist():
# Before writing the zippedFile into p_folder, create the intermediary
# subfolder(s) if needed.
fileName = None
if zippedFile.endswith('/') or zippedFile.endswith(os.sep):
# This is an empty folder. Create it nevertheless. If zippedFile
# starts with a '/', os.path.join will consider it an absolute
# path and will throw away folder.
os.makedirs(os.path.join(folder, zippedFile.lstrip('/')))
else:
fileName = os.path.basename(zippedFile)
folderName = os.path.dirname(zippedFile)
fullFolderName = folder
if folderName:
fullFolderName = os.path.join(fullFolderName, folderName)
if not os.path.exists(fullFolderName):
os.makedirs(fullFolderName)
# Unzip the file in folder
if fileName:
fullFileName = os.path.join(fullFolderName, fileName)
f = open(fullFileName, 'wb')
fileContent = zipFile.read(zippedFile)
if odf and not folderName:
# content.xml and others may reside in subfolders. Get only the
# one in the root folder.
if fileName == 'content.xml':
res['content.xml'] = fileContent
elif fileName == 'styles.xml':
res['styles.xml'] = fileContent
elif fileName == 'mimetype':
res['mimetype'] = fileContent
f.write(fileContent)
f.close()
zipFile.close()
return res
# ------------------------------------------------------------------------------
def zip(f, folder, odf=False):
'''Zips the content of p_folder into the zip file whose (preferably)
absolute filename is p_f. If p_odf is True, p_folder is considered to
contain the standard content of an ODF file (content.xml,...). In this
case, some rules must be respected while building the zip (see below).'''
# Remove p_f if it exists
if os.path.exists(f): os.remove(f)
try:
zipFile = zipfile.ZipFile(f, 'w', zipfile.ZIP_DEFLATED)
except RuntimeError:
zipFile = zipfile.ZipFile(f, 'w')
# If p_odf is True, insert first the file "mimetype" (uncompressed), in
# order to be compliant with the OpenDocument Format specification,
# section 17.4, that expresses this restriction. Else, libraries like
# "magic", under Linux/Unix, are unable to detect the correct mimetype for
# a pod result (it simply recognizes it as a "application/zip" and not a
# "application/vnd.oasis.opendocument.text)".
if odf:
mimetypeFile = os.path.join(folder, 'mimetype')
# This file may not exist (presumably, ods files from Google Drive)
if not os.path.exists(mimetypeFile):
f = file(mimetypeFile, 'w')
f.write(mimeTypes[os.path.splitext(f)[-1][1:]])
f.close()
zipFile.write(mimetypeFile, 'mimetype', zipfile.ZIP_STORED)
for dir, dirnames, filenames in os.walk(folder):
for name in filenames:
folderName = dir[len(folder)+1:]
# For p_odf files, ignore file "mimetype" that was already inserted
if odf and (folderName == '') and (name == 'mimetype'): continue
zipFile.write(os.path.join(dir,name), os.path.join(folderName,name))
if not dirnames and not filenames:
# This is an empty leaf folder. We must create an entry in the
# zip for him.
folderName = dir[len(folder):]
zInfo = zipfile.ZipInfo("%s/" % folderName, time.localtime()[:6])
zInfo.external_attr = 48
zipFile.writestr(zInfo, '')
zipFile.close()
# ------------------------------------------------------------------------------