如何仅通过Python字符串和不使用临时文件程序性地创建嵌套目录和文件的tar归档?

4 投票
3 回答
3869 浏览
提问于 2025-04-17 09:05

我想用Python创建一个tar压缩包,并且里面有层级目录结构,文件的内容是用字符串表示的。我看过这个问题,里面介绍了如何把字符串作为文件添加进去,但没有讲怎么添加目录。我想知道怎么在不实际创建目录的情况下,动态地把目录添加到tar压缩包里。

类似于:

archive.tgz:
    file1.txt
    file2.txt
    dir1/
        file3.txt
        dir2/
            file4.txt

3 个回答

1

看一下tar文件格式,这看起来是可以做到的。每个子目录里的文件会用相对路径名来命名,比如说dir1/file3.txt

唯一需要注意的是,你必须在放文件之前先定义好每个目录(tar不会自动创建需要的子目录)。有一个特殊的标志可以用来标识tar文件中的条目是目录,但为了兼容老版本,tar也接受以/结尾的文件名来表示目录,所以你可以用同样的方法,把dir1/作为一个长度为零的字符串的文件添加进去。

2

对之前那个很有帮助的被接受的答案做了一点小改动,这样它在Python 3和Python 2中都能用,并且更接近原问题的例子:

from io import BytesIO
import tarfile
import time

# create and open empty tar file
tar = tarfile.open("test.tgz", "w:gz")

# Add a file
file1_contents = BytesIO("hello 1".encode())
finfo1 = tarfile.TarInfo(name='file1.txt')
finfo1.size = len(file1_contents.getvalue())
finfo1.mtime = time.time()
tar.addfile(tarinfo=finfo1, fileobj=file1_contents)

# create directory in the tar file
dinfo = tarfile.TarInfo(name='dir')
dinfo.type = tarfile.DIRTYPE
dinfo.mode = 0o755
dinfo.mtime = time.time()
tar.addfile(tarinfo=dinfo)

# add a file to the new directory in the tar file
file2_contents = BytesIO("hello 2".encode())
finfo2 = tarfile.TarInfo(name='dir/file2.txt')
finfo2.size = len(file2_contents.getvalue())
finfo2.mtime = time.time()
tar.addfile(tarinfo=finfo2, fileobj=file2_contents)

tar.close()

具体来说,我更新了八进制的写法,参考了PEP 3127 -- 整数字面量支持和语法,改用了BytesIO from io,用getvalue替代了buf,并且用open替代了TarFile,这样可以像例子中那样显示压缩后的输出。(使用上下文管理器的方式(with ... as tar:)在Python 2和Python 3中都能用,但我在Python 2的环境中复制粘贴时遇到问题,所以没有改这个。)在Python 2.7.15+和Python 3.7.3上测试过。

11

在这个问题的例子基础上,你可以这样做:

import tarfile
import StringIO
import time

tar = tarfile.TarFile("test.tar", "w")

string = StringIO.StringIO()
string.write("hello")
string.seek(0)

info = tarfile.TarInfo(name='dir')
info.type = tarfile.DIRTYPE
info.mode = 0755
info.mtime = time.time()
tar.addfile(tarinfo=info)

info = tarfile.TarInfo(name='dir/foo')
info.size=len(string.buf)
info.mtime = time.time()
tar.addfile(tarinfo=info, fileobj=string)

tar.close()

要注意mode属性,因为默认值可能不包括目录所有者的执行权限,而这些权限是进入目录和获取其内容所必需的。

撰写回答