'''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() # ------------------------------------------------------------------------------