且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

Golang:将文件附加到现有的tar档案

更新时间:2022-10-14 23:01:19




tar档案由一系列512字节的记录组成。每个文件系统
对象都需要一个包含基本元数据(路径名,
所有者,权限等)的头记录和零个或多个包含任何文件
数据的记录。存档的结尾由两个记录表示,其中
完全由零字节组成。



执行添加这两个零填充记录的执行发生在这里为了避开 tar 文件格式的预告片(基本上没有任何东西是1024个字节),你可以使用
$ b 替换这些行:

  f,err = os.OpenFile(/ home / jeff / Desktop / test.tar,os .O_APPEND | os.O_WRONLY,os.ModePerm)
if err!= nil {
log.Fatalln(err)
}
tw = tar.NewWriter(f)

与:

  f,err = os.OpenFile(/ home / jeff / Desktop / test.tar,os.O_RDWR,os.ModePerm)
if err!= nil {
log.Fatalln( err)
}
if _,err = f.Seek(-2 log.Fatalln(err)
}
tw = tar.NewWriter(f)

它打开文件读/写(而不是追加/只写),然后在文件结束前寻找1024个字节并从那里写入。



它很有效,但它是一种可怕的黑客行为。



编辑:在理解了 tar 文件规范后,我不再相信这是一种破解。

完整代码: http://play.golang.org/p/0zRScmY4AC


How would I append a file to an existing tar archive in Go? I don't see anything obvious in the docs on how to do it.

I have a tar file that has already been created and I want to add more to it after it has already been closed.

EDIT

Altering the example in the docs and following the answer given, I'm still not getting the expected result. The first three files are being written to the tar but when I close and open up the file again to write to it, the new file is never being written. The code runs fine. I don't know what I'm missing.

The following code gives me a tar file with three files in it: readme.txt, gopher.txt, todo.txt. foo.bar never gets written.

package main

import (
    "archive/tar"
    "log"
    "os"
)

func main() {
    f, err := os.Create("/home/jeff/Desktop/test.tar")
    if err != nil {
        log.Fatalln(err)
    }

    tw := tar.NewWriter(f)

    var files = []struct {
        Name, Body string
    }{
        {"readme.txt", "This archive contains some text files."},
        {"gopher.txt", "Gopher names:\nGeorge\nGeoffrey\nGonzo"},
        {"todo.txt", "Get animal handling licence."},
    }
    for _, file := range files {
        hdr := &tar.Header{
            Name: file.Name,
            Size: int64(len(file.Body)),
        }
        if err := tw.WriteHeader(hdr); err != nil {
            log.Fatalln(err)
        }
        if _, err := tw.Write([]byte(file.Body)); err != nil {
            log.Fatalln(err)
        }
    }
    if err := tw.Close(); err != nil {
        log.Fatalln(err)
    }
    f.Close()

    // Open up the file and append more things to it

    f, err = os.OpenFile("/home/jeff/Desktop/test.tar", os.O_APPEND|os.O_WRONLY, os.ModePerm)
    if err != nil {
        log.Fatalln(err)
    }
    tw = tar.NewWriter(f)

    test := "this is a test"

    hdr := &tar.Header{
        Name: "foo.bar",
        Size: int64(len(test)),
    }

    if err := tw.WriteHeader(hdr); err != nil {
        log.Fatalln(err)
    }

    if _, err := tw.Write([]byte(test)); err != nil {
        log.Fatalln(err)
    }

    if err := tw.Close(); err != nil {
        log.Fatalln(err)
    }
    f.Close()

}

The tar file specification states:

A tar archive consists of a series of 512-byte records. Each file system object requires a header record which stores basic metadata (pathname, owner, permissions, etc.) and zero or more records containing any file data. The end of the archive is indicated by two records consisting entirely of zero bytes.

The Go implementation of adding these two zero filled records happens here .

To get around the tar file format trailer (basically 1024 bytes of nothing) you could replace the lines:

f, err = os.OpenFile("/home/jeff/Desktop/test.tar", os.O_APPEND|os.O_WRONLY, os.ModePerm)
if err != nil {
    log.Fatalln(err)
}
tw = tar.NewWriter(f)

With:

f, err = os.OpenFile("/home/jeff/Desktop/test.tar", os.O_RDWR, os.ModePerm)
if err != nil {
    log.Fatalln(err)
}
if _, err = f.Seek(-2<<9, os.SEEK_END); err != nil {
    log.Fatalln(err)
}
tw = tar.NewWriter(f)

It opens the file read / write (instead of append / write-only) and then seeks to 1024 bytes before the end of the file and writes from there.

It works, but it is a horrible hack.

EDIT: After understanding the tar file spec a little better, I no longer believe this is such a hack.

Full code: http://play.golang.org/p/0zRScmY4AC