How to compress a file with shutil.make_archive in python? - python

I want to compress one text file using shutil.make_archive command. I am using the following command:
shutil.make_archive('gzipped'+fname, 'gztar', os.path.join(os.getcwd(), fname))
OSError: [Errno 20] Not a directory: '/home/user/file.txt'
I tried several variants but it keeps trying to compress the whole folders. How to do it correctly?

Actually shutil.make_archive can make one-file archive! Just pass path to target directory as root_dir and target filename as base_dir.
Try this:
import shutil
file_to_zip = 'test.txt' # file to zip
target_path = 'C:\\test_yard\\' # dir, where file is
try:
shutil.make_archive(target_path + 'archive', 'zip', target_path, file_to_zip)
except OSError:
pass

shutil can't create an archive from one file. You can use tarfile, instead:
tar = tarfile.open(fname + ".tar.gz", 'w:qz')
os.chdir('/home/user')
tar.add("file.txt")
tar.close()
or
tar = tarfile.open(fname + ".tar.gz", 'w:qz')
tar.addfile(tarfile.TarInfo("/home/user/file.txt"), "/home/user/file.txt")
tar.close()

Try this and Check shutil
copy your file to a directory.
cd directory
shutil.make_archive('gzipped', 'gztar', os.getcwd())

#CommonSense had a good answer, but the file will always be created zipped inside its parent directories. If you need to create a zipfile without the extra directories, just use the zipfile module directly
import os, zipfile
inpath = "test.txt"
outpath = "test.zip"
with zipfile.ZipFile(outpath, "w", compression=zipfile.ZIP_DEFLATED) as zf:
zf.write(inpath, os.path.basename(inpath))

If you don't mind doing a file copy op:
def single_file_to_archive(full_path, archive_name_no_ext):
tmp_dir = tempfile.mkdtemp()
shutil.copy2(full_path, tmp_dir)
shutil.make_archive(archive_name_no_ext, "zip", tmp_dir, '.')
shutil.rmtree(tmp_dir)

Archiving a directory to another destination was a pickle for me but shutil.make_archive not zipping to correct destination helped a lot.
from shutil import make_archive
make_archive(
base_name=path_to_directory_to_archive},
format="gztar",
root_dir=destination_path,
base_dir=destination_path)

Related

Why I got infinity loop to add files in archive?

I started learn python and trying to create 'backup' app. Want to add files from chosen directory in zip archive, but I don't understand why zipfile.write adding the same files from directory in arhive non-stop? Also it add itself to archive.
import zipfile, os, pathlib, time
from os.path import basename
now = time.strftime('%H%M%S')
source3 = 'F:\oneMoreTry'
# create a ZipFile object
with zipfile.ZipFile(now + '.zip', 'w') as zipObj:
# Iterate over all the files in directory
for folderName, subfolders, filenames in os.walk(source3):
for filename in filenames:
# create complete filepath of file in directory
filePath = os.path.join(folderName, filename)
# Add file to zip
zipObj.write(filePath, basename(filePath))

Zip single file

I am trying to zip a single file in python. For whatever reason, I'm having a hard time getting down the syntax. What I am trying to do is keep the original file and create a new zipped file of the original (like what a Mac or Windows would do if you archive a file).
Here is what I have so far:
import zipfile
myfilepath = '/tmp/%s' % self.file_name
myzippath = myfilepath.replace('.xml', '.zip')
zipfile.ZipFile(myzippath, 'w').write(open(myfilepath).read()) # does not zip the file properly
The correct way to zip file is:
zipfile.ZipFile('hello.zip', mode='w').write("hello.csv")
# assume your xxx.py under the same dir with hello.csv
The python official doc says:
ZipFile.write(filename, arcname=None, compress_type=None)
Write the file named filename to the archive, giving it the archive name arcname
You pass open(filename).read() into write(). open(filename).read() is a single string that contains the whole content of file filename, it would throw FileNotFoundError because it is trying to find a file named with the string content.
If the file to be zipped (filename) is in a different directory called pathname, you should use the arcname parameter. Otherwise, it will recreate the full folder hierarchy to the file folder.
from zipfile import ZipFile
import os
with ZipFile(zip_file, 'w') as zipf:
zipf.write(os.path.join(pathname,filename), arcname=filename)
Try calling zipfile.close() afterwards?
from zipfile import ZipFile
zipf = ZipFile("main.zip","w", zipfile.ZIP_DEFLATED)
zipf.write("main.json")
zipf.close()
Since you also want to specify the directory try using os.chdir:
#!/usr/bin/python
from zipfile import ZipFile
import os
os.chdir('/path/of/target/and/destination')
ZipFile('archive.zip', 'w').write('original_file.txt')
Python zipfile : Work with Zip archives
Python Miscellaneous operating system interfaces

proper way for zipping files in python

I'm trying to create a zip file by zipping couple text files from a specific directory. My code looks like the following:
import zipfile,os
project='C:/Users/user1/Documents/work/filesToZip'
dirlist = os.listdir(project)
print dirlist
zip_name = zipfile.ZipFile(os.path.join(project,'jobs.zip'),'w')
for file in dirlist:
zip_name.write(os.path.join(project,file))
zip_name.close()
the code runs fine and it creates the zip file, the only problem is when I open the zip file I found the whole directory structure is zipped. i.e. when I open the file I will find Users open it then user1 open it then Documents open it then work then filesToZip then I find the files I want to zip. my question is how can I get red of the file structure so when I open the zip file I find the files I zipped right away?
Thanks in advance!
ZipFile.write has an optional second parameter archname
which does exactly what you want.
import zipfile,os
project='C:/Users/user1/Documents/work/filesToZip'
# prevent adding zip to itself if the old zip is left in the directory
zip_path = os.path.join(project,'jobs.zip')
if os.path.exists(zip_path):
os.unlink(zip_path);
dirlist = os.listdir(project)
zip_file = zipfile.ZipFile(zip_path, 'w')
for file_name in dirlist:
zip_file.write(os.path.join(project, file_name), file_name)
zip_file.close()
For python 2.7+ you can use shutil instead:
from shutil import make_archive
make_archive(
'zipfile_name',
'zip', # the archive format - or tar, bztar, gztar
root_dir=None, # root for archive - current working dir if None
base_dir=None) # start archiving from here - cwd if None too
This way you can explicitly specify which directory should be the root_dir and which should be the base_dir. If root_dir and base_dir are not the same, it will only zip the files in base_dir but preserve the directory structure up to root_dir
import zipfile,os
project='C:/Users/user1/Documents/work/filesToZip'
original_dir= os.getcwd()
os.chdir(project)
dirlist = os.listdir(".")
print dirlist
zip_name = zipfile.ZipFile('./jobs.zip','w')
for file in dirlist:
zip_name.write('./'+file)
zip_name.close()
os.chdir(original_dir)

How can I create a zipped file in Python that when unzipped will give me the raw files rather than a folder? [duplicate]

I have two files in two different directories, one is '/home/test/first/first.pdf', the other is '/home/text/second/second.pdf'. I use following code to compress them:
import zipfile, StringIO
buffer = StringIO.StringIO()
first_path = '/home/test/first/first.pdf'
second_path = '/home/text/second/second.pdf'
zip = zipfile.ZipFile(buffer, 'w')
zip.write(first_path)
zip.write(second_path)
zip.close()
After I open the zip file that I created, I have a home folder in it, then there are two sub-folders in it, first and second, then the pdf files. I don't know how to include only two pdf files instead of having full path zipped into the zip archive. I hope I make my question clear, please help.
The zipfile write() method supports an extra argument (arcname) which is the archive name to be stored in the zip file, so you would only need to change your code with:
from os.path import basename
...
zip.write(first_path, basename(first_path))
zip.write(second_path, basename(second_path))
zip.close()
When you have some spare time reading the documentation for zipfile will be helpful.
I use this function to zip a directory without include absolute path
import zipfile
import os
def zipDir(dirPath, zipPath):
zipf = zipfile.ZipFile(zipPath , mode='w')
lenDirPath = len(dirPath)
for root, _ , files in os.walk(dirPath):
for file in files:
filePath = os.path.join(root, file)
zipf.write(filePath , filePath[lenDirPath :] )
zipf.close()
#end zipDir
I suspect there might be a more elegant solution, but this one should work:
def add_zip_flat(zip, filename):
dir, base_filename = os.path.split(filename)
os.chdir(dir)
zip.write(base_filename)
zip = zipfile.ZipFile(buffer, 'w')
add_zip_flat(zip, first_path)
add_zip_flat(zip, second_path)
zip.close()
You can override the filename in the archive with the arcname parameter:
with zipfile.ZipFile(file="sample.zip", mode="w", compression=zipfile.ZIP_DEFLATED) as out_zip:
for f in Path.home().glob("**/*.txt"):
out_zip.write(f, arcname=f.name)
Documentation reference: https://docs.python.org/3/library/zipfile.html#zipfile.ZipFile.write
Can be done that way also (this allow for creating archives >2GB)
import os, zipfile
def zipdir(path, ziph):
"""zipper"""
for root, _, files in os.walk(path):
for file_found in files:
abs_path = root+'/'+file_found
ziph.write(abs_path, file_found)
zipf = zipfile.ZipFile(DEST_FILE.zip, 'w', zipfile.ZIP_DEFLATED, allowZip64=True)
zipdir(SOURCE_DIR, zipf)
zipf.close()
As João Pinto said, the arcname argument of ZipFile.write is what you need. Also, reading the documentation of pathlib is helpful. You can easily get the relative path to something also with pathlib.Path.relative_to, no need to switch to os.path.
import zipfile
from pathlib import Path
folder_to_compress = Path("/path/to/folder")
path_to_archive = Path("/path/to/archive.zip")
with zipfile.ZipFile(
path_to_archive,
mode="w",
compression=zipfile.ZIP_DEFLATED,
compresslevel=7,
) as zip:
for file in folder_to_compress.rglob("*"):
relative_path = file.relative_to(folder_to_compress)
print(f"Packing {file} as {relative_path}")
zip.write(file, arcname=relative_path)

How to eliminate absolute path in zip archive if absolute paths for files are provided?

I have two files in two different directories, one is '/home/test/first/first.pdf', the other is '/home/text/second/second.pdf'. I use following code to compress them:
import zipfile, StringIO
buffer = StringIO.StringIO()
first_path = '/home/test/first/first.pdf'
second_path = '/home/text/second/second.pdf'
zip = zipfile.ZipFile(buffer, 'w')
zip.write(first_path)
zip.write(second_path)
zip.close()
After I open the zip file that I created, I have a home folder in it, then there are two sub-folders in it, first and second, then the pdf files. I don't know how to include only two pdf files instead of having full path zipped into the zip archive. I hope I make my question clear, please help.
The zipfile write() method supports an extra argument (arcname) which is the archive name to be stored in the zip file, so you would only need to change your code with:
from os.path import basename
...
zip.write(first_path, basename(first_path))
zip.write(second_path, basename(second_path))
zip.close()
When you have some spare time reading the documentation for zipfile will be helpful.
I use this function to zip a directory without include absolute path
import zipfile
import os
def zipDir(dirPath, zipPath):
zipf = zipfile.ZipFile(zipPath , mode='w')
lenDirPath = len(dirPath)
for root, _ , files in os.walk(dirPath):
for file in files:
filePath = os.path.join(root, file)
zipf.write(filePath , filePath[lenDirPath :] )
zipf.close()
#end zipDir
I suspect there might be a more elegant solution, but this one should work:
def add_zip_flat(zip, filename):
dir, base_filename = os.path.split(filename)
os.chdir(dir)
zip.write(base_filename)
zip = zipfile.ZipFile(buffer, 'w')
add_zip_flat(zip, first_path)
add_zip_flat(zip, second_path)
zip.close()
You can override the filename in the archive with the arcname parameter:
with zipfile.ZipFile(file="sample.zip", mode="w", compression=zipfile.ZIP_DEFLATED) as out_zip:
for f in Path.home().glob("**/*.txt"):
out_zip.write(f, arcname=f.name)
Documentation reference: https://docs.python.org/3/library/zipfile.html#zipfile.ZipFile.write
Can be done that way also (this allow for creating archives >2GB)
import os, zipfile
def zipdir(path, ziph):
"""zipper"""
for root, _, files in os.walk(path):
for file_found in files:
abs_path = root+'/'+file_found
ziph.write(abs_path, file_found)
zipf = zipfile.ZipFile(DEST_FILE.zip, 'w', zipfile.ZIP_DEFLATED, allowZip64=True)
zipdir(SOURCE_DIR, zipf)
zipf.close()
As João Pinto said, the arcname argument of ZipFile.write is what you need. Also, reading the documentation of pathlib is helpful. You can easily get the relative path to something also with pathlib.Path.relative_to, no need to switch to os.path.
import zipfile
from pathlib import Path
folder_to_compress = Path("/path/to/folder")
path_to_archive = Path("/path/to/archive.zip")
with zipfile.ZipFile(
path_to_archive,
mode="w",
compression=zipfile.ZIP_DEFLATED,
compresslevel=7,
) as zip:
for file in folder_to_compress.rglob("*"):
relative_path = file.relative_to(folder_to_compress)
print(f"Packing {file} as {relative_path}")
zip.write(file, arcname=relative_path)

Categories

Resources