Golang:从tar文件中提取的文件引发权限错误。

huangapple go评论69阅读模式
英文:

Golang: file extracted from tar throws permissions error

问题

我已经写了以下代码来压缩文件,代码可以正常工作,但是奇怪的是,如果我解压缩归档文件,文件权限就会丢失,所以我无法读取它,除非我再使用chmod命令更改文件权限:

package main

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

func main() {

	c, err := os.Create("/path/to/tar/file/test.tar")
	if err != nil {
		log.Fatalln(err)
	}

	tw := tar.NewWriter(c)

	f, err := os.Open("sample.txt")
	if err != nil {
		log.Fatalln(err)
	}

	fi, err := f.Stat()
	if err != nil {
		log.Fatalln(err)
	}

	hdr := &tar.Header{Name: f.Name(),
		Size: fi.Size(),
	}
	if err := tw.WriteHeader(hdr); err != nil {
		log.Fatalln(err)
	}

	r, err := ioutil.ReadFile("sample.txt")
	if err != nil {
		log.Fatalln(err)
	}

	if _, err := tw.Write(r); err != nil {
		log.Fatalln(err)
	}
	if err := tw.Close(); err != nil {
		log.Fatalln(err)
	}

}

你有什么想法,我做错了什么吗?

英文:

I've written the following code to tar a file, code works but strangely if I untar the archive the file permissions are gone so I can't read it unless I then chmod the file:

package main

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

func main() {

	c, err := os.Create("/path/to/tar/file/test.tar")
	if err != nil {
		log.Fatalln(err)
	}

	tw := tar.NewWriter(c)

	f, err := os.Open("sample.txt")
	if err != nil {
		log.Fatalln(err)
	}

	fi, err := f.Stat()
	if err != nil {
		log.Fatalln(err)
	}

	hdr := &tar.Header{Name: f.Name(),
		Size: fi.Size(),
	}
	if err := tw.WriteHeader(hdr); err != nil {
		log.Fatalln(err)
	}

	r, err := ioutil.ReadFile("sample.txt")
	if err != nil {
		log.Fatalln(err)
	}

	if _, err := tw.Write(r); err != nil {
		log.Fatalln(err)
	}
	if err := tw.Close(); err != nil {
		log.Fatalln(err)
	}

}

Any idea what I'm doing wrong?

答案1

得分: 3

你没有保留文件的原始权限。你手动创建了一个头部,并且只指定了名称和大小。相反,应该使用tar.FileInfoHeader来构建头部。

package main

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

func main() {
    c, err := os.Create("/path/to/tar/file/test.tar")
    if err != nil {
        log.Fatalln(err)
    }

    tw := tar.NewWriter(c)

    f, err := os.Open("sample.txt")
    if err != nil {
        log.Fatalln(err)
    }

    fi, err := f.Stat()
    if err != nil {
        log.Fatalln(err)
    }
    // 从 FileInfo 创建头部
    hdr, err := tar.FileInfoHeader(fi, "")
    if err != nil {
        log.Fatalln(err)
    }
    if err := tw.WriteHeader(hdr); err != nil {
        log.Fatalln(err)
    }
    // 使用 io.Copy 代替将整个文件读入内存
    r, err := io.Copy(tw, f)
    if err != nil {
        log.Fatalln(err)
    }
    log.Printf("写入了 %d 字节\n", r)
}

另外请注意,我使用了io.Copy将数据从文件(一个io.Reader)复制到 tar 写入器(一个io.Writer)。这对于较大的文件效果更好。

还要特别注意文档中的这个说明:

由于 os.FileInfo 的 Name 方法只返回所描述文件的基本名称,可能需要修改返回的头部的 Name 字段以提供文件的完整路径名。

在这个简单的示例中,你只是使用了_sample.txt_,所以不会遇到问题。如果你想在 tar 中保留目录结构,可能需要修改头部的 Name 字段。

英文:

You're not preserving the original permissions of the file. You're manually creating a header, and specifying only the name and size. Instead, use tar.FileInfoHeader to build the header.

package main

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

func main() {
    c, err := os.Create("/path/to/tar/file/test.tar")
    if err != nil {
        log.Fatalln(err)
    }

    tw := tar.NewWriter(c)

    f, err := os.Open("sample.txt")
    if err != nil {
        log.Fatalln(err)
    }

    fi, err := f.Stat()
    if err != nil {
        log.Fatalln(err)
    }
    // create header from FileInfo
    hdr, err := tar.FileInfoHeader(fi, "")
    if err != nil {
        log.Fatalln(err)
    }
    if err := tw.WriteHeader(hdr); err != nil {
        log.Fatalln(err)
    }
    // instead of reading the whole file into memory, prefer io.Copy
    r, err := io.Copy(tw, f)
    if err != nil {
        log.Fatalln(err)
    }
    log.Printf("Wrote %d bytes\n", r)
}

Also note that I used io.Copy to copy data from the file (an io.Reader) to the tar writer (an io.Writer). This will work much better for larger files.

Also - pay special attention to this note from the docs:

> Because os.FileInfo's Name method returns only the base name of the file it describes, it may be necessary to modify the Name field of the returned header to provide the full path name of the file.

In this simple example, you're just using sample.txt so you shouldn't run into trouble. If you wanted to preserve a directory structure in your tar, you may have to modify the Name field in the header.

huangapple
  • 本文由 发表于 2015年2月14日 23:00:46
  • 转载请务必保留本文链接:https://go.coder-hub.com/28516864.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定