2016-07-19 35 views
2

複数のディレクトリやファイルからtar.gzファイルを作成しようとしています。ファイルやディレクトリからtar.gzファイルを作成しようとすると `write too long`エラーが発生する

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 

エラーを、とき私はそれがすべて動作する削除します。

は、任意のアイデアをアイデアを使い果たし、解決策を見つけようとし、この上で多くの時間を無駄に...

おかげ

答えて

5

私はtar.FileInfoHeaderドキュメントでより密接に見てまで、私は同様の問題を抱えていたが:

FileInfoHeaderが作成されます部分的に配置されたヘッダーをfiから取得します。 fiがシンボリックリンクを記述している場合、FileInfoHeaderはリンクをリンクターゲットとして記録します。 fiがディレクトリを記述すると、その名前にスラッシュが追加されます。 os.FileInfoのNameメソッドは、それが記述するファイルのベース名のみを返すので、返されるヘッダーのNameフィールドを変更して、ファイルの絶対パス名を指定する必要があります。

基本的に、FileInfoHeaderはあなたがWriteHeaderでそれを書く前に、すべてのヘッダフィールドを記入することは保証されません、あなたが実装を見ればサイズフィールドは、regularファイルに設定されています。コードスニペットはディレクトリのみを扱うように見えます。これは、他の非正規ファイルに遭遇した場合は、ゼロのサイズのヘッダーを書き込み、ディスク上の潜在的に非ゼロサイズの特殊ファイルをtarにコピーしようとします。 GoはErrWriteTooLongを返して、壊れたタールを作成しないようにします。

私はこのことを考え出して以来、この問題を抱えていませんでした。

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 
}) 
+0

はいこれは正しい回答です –

+0

ファイルがディスク上で変更され、長くなった可能性もあります。 –

0

書き込みは、tarアーカイブ内の現在のエントリに書き込みます。 WriteHeaderの後にhdr.Sizeバイト以上が書き込まれると、ErrWriteTooLongというエラーが返されます。

ヘッダーに追加できるオプションはSizeです。 ...

参照してください、それを試してみましたが、おそらくそれは助けなかったのもhttps://golang.org/pkg/archive/tar/