因此,我试图从多个目录和文件中缓存一个tar.gz文件文件。与以下事物的用法相同的事物:
tar -cvzf sometarfile.tar.gz somedir/ someotherdir/ somefile.json somefile.xml
假设目录中有其他目录。我把这作为一种投入:
paths := []string{
"somedir/",
"someotherdir/",
"somefile.json",
"somefile.xml",
}
并使用这些方法:
func TarFilesDirs(paths []string, tarFilePath string ) error {
// set up the output file
file, err := os.Create(tarFilePath)
if err != nil {
return err
}
defer file.Close()
// set up the gzip writer
gz := gzip.NewWriter(file)
defer gz.Close()
tw := tar.NewWriter(gz)
defer tw.Close()
// add each file/dir as needed into the current tar archive
for _,i := range paths {
if err := tarit(i, tw); err != nil {
return err
}
}
return nil
}
func tarit(source string, tw *tar.Writer) error {
info, err := os.Stat(source)
if err != nil {
return nil
}
var baseDir string
if info.IsDir() {
baseDir = filepath.Base(source)
}
return filepath.Walk(source,
func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
header, err := tar.FileInfoHeader(info, info.Name())
if err != nil {
return err
}
if baseDir != "" {
header.Name = filepath.Join(baseDir, strings.TrimPrefix(path, source))
}
if err := tw.WriteHeader(header); err != nil {
return err
}
if info.IsDir() {
return nil
}
file, err := os.Open(path)
if err != nil {
return err
}
defer file.Close()
_, err = io.Copy(tw, file)
if err != nil {
log.Println("failing here")
return err
}
return err
})
}
问题:如果目录很大,则为:
archive/tar: write too long
错误,当我删除它时,一切正常。
想法用完了,花了很多时间在这个问题上,试图找到解决办法.
有什么想法吗?
谢谢
发布于 2016-10-12 16:23:01
我也遇到了类似的问题,直到我更仔细地查看了tar.FileInfoHeader文档:
FileInfoHeader从fi创建一个部分填充的标头。如果fi描述符号链接,则FileInfoHeader将链接记录为链接目标。如果fi描述一个目录,则在名称后面追加一个斜杠。因为os.FileInfo的Name方法只返回它描述的文件的基本名称,所以可能需要修改返回头的name字段以提供文件的完整路径名。
本质上,FileInfoHeader并不保证在用WriteHeader编写它之前填充所有的头字段,而且如果您查看实现,大小字段只会设置在规则性文件上。您的代码片段似乎只处理目录,这意味着如果您遇到任何其他非常规文件,您可以编写大小为零的头文件,然后尝试将磁盘上一个潜在的非零大小的特殊文件复制到tar中。Go返回ErrWriteTooLong以阻止您创建一个损坏的tar。
我想出了这个问题,从那以后就再也没有这个问题了。
if err := filepath.Walk(directory, func(path string, info os.FileInfo, err error) error {
if err != nil {
return check(err)
}
var link string
if info.Mode()&os.ModeSymlink == os.ModeSymlink {
if link, err = os.Readlink(path); err != nil {
return check(err)
}
}
header, err := tar.FileInfoHeader(info, link)
if err != nil {
return check(err)
}
header.Name = filepath.Join(baseDir, strings.TrimPrefix(path, directory))
if err = tw.WriteHeader(header); err != nil {
return check(err)
}
if !info.Mode().IsRegular() { //nothing more to do for non-regular
return nil
}
fh, err := os.Open(path)
if err != nil {
return check(err)
}
defer fh.Close()
if _, err = io.CopyBuffer(tw, fh, buf); err != nil {
return check(err)
}
return nil
})
发布于 2016-07-19 09:58:09
写入tar存档中的当前条目。如果在ErrWriteTooLong之后写入的hdr.Size字节多于hdr.Size字节,那么Write将返回错误WriteHeader。
有一个Size
选项可以添加到标题中。还没试过但也许这有帮助..。
发布于 2020-12-03 18:19:59
由于您只在一个大目录中看到这个问题,我认为下面的修复可能没有帮助,但这将解决从可能不断增长的文件中创建tar的问题。
在我的例子中,问题是当我们创建tar头时,header.Size (在tar.FileInfoHeader内部)在那个时刻得到了文件大小(info.Size())的设置。
当我们稍后在代码中打开相关文件(os.Open)并复制其内容(io.Copy)时,我们可能会复制更多的数据,而不是前面将tar标头大小设置为的数据,因为在此期间该文件可能已经增长。
这段代码将确保在将tar标头大小设置为:
_, err = io.**CopyN**(tw, file, info.Size())
if err != nil {
log.Println("failing here")
return err
}
https://stackoverflow.com/questions/38454850
复制相似问题