使用Go的archive/zip创建一个包含Unicode文件名的zip归档文件

4
package main

import (
    "archive/zip"
    "fmt"
    "io"
    "os"
    "path/filepath"
    "strings"
)

func main() {
    var (
        Path = os.Args[1]
        Name = os.Args[2]
    )

    File, _ := os.Create(Name)
    PS := strings.Split(Path, "\\")
    PathName := strings.Join(PS[:len(PS)-1], "\\")
    os.Chdir(PathName)
    Path = PS[len(PS)-1]
    defer File.Close()
    Zip := zip.NewWriter(File)
    defer Zip.Close()
    walk := func(Path string, info os.FileInfo, err error) error {
        if err != nil {
            fmt.Println(err)
            return err
        }
        if info.IsDir() {
            return nil
        }
        Src, _ := os.Open(Path)
        defer Src.Close()
        fmt.Println(Path)
        FileName, _ := Zip.Create(Path)
        io.Copy(FileName, Src)
        Zip.Flush()
        return nil
    }
    if err := filepath.Walk(Path, walk); err != nil {
        fmt.Println(err)
    }
}

这是我的目录路径:

-----root
    |---2015-05(dir)
         |---中文.go
    |---package(dir)
    |---你好.go

当我使用这个代码目录时,中文会出现乱码。谁能帮我解决这个问题。

2个回答

11
问题在于,默认情况下,Zip规范只允许使用ASCII字符作为zip条目名称,更具体地说:(来源:附录D

附录D.1 ZIP格式历史上仅支持原始IBM PC字符编码集,通常称为IBM代码页437。这限制了仅存储原始MS-DOS值范围内的文件名字符,并且不适当地支持其他字符编码或语言的文件名。为解决此限制,此规范将支持以下更改。

后来增加了对Unicode名称的支持。这可以用一个特殊的位标记,称为通用目的位11,也称为语言编码标志(EFS)

4.4.4节

通用位标志 - 位11 - 语言编码标志(EFS)。如果设置了此位,则必须使用UTF-8对此文件的文件名和注释字段进行编码。

附录D.2

如果未设置通用位11,则文件名和注释应符合原始ZIP字符编码。如果设置了通用位11,则文件名和注释必须支持Unicode标准,版本4.1.0或更高版本,并使用由UTF-8存储规范定义的字符编码形式。Unicode标准由Unicode Consortium(www.unicode.org)发布。预期在ZIP文件中存储的UTF-8编码数据不包括字节顺序标记(BOM)。

general purpose bit flag存在并受Go支持:它是FileHeader结构体的Flags字段。不幸的是,Go没有设置此位的方法,并且默认情况下为0。

所以,为了添加对Unicode名称的支持,最简单的方法是将第11位设置为1。而不是
FileName, _ := Zip.Create(Path)

以以下内容开始您的zip条目:

h := &zip.FileHeader{Name:Path, Method: zip.Deflate, Flags: 0x800}
FileName, _ := Zip.CreateHeader(h)

第一行创建了一个FileHeader,其中将Flags字段设置为0x800第11位),这告诉我们文件名将使用UTF-8编码(当Go将string写入io.Writer时会这样做)。 注意: 这样做可以保留UTF-8文件名,但并非所有的zip读取器/提取器都支持它。例如,在Windows上,Windows文件处理程序——Windows Explorer不会将其解码为UTF-8,但是更严谨的Zip处理程序(例如 SecureZip)将看到UTF-8文件名,并正确地提取文件名(使用UTF-8解码)。

1
注意:自Go 1.10以来,如果您使用UTF-8字符,则会自动添加此标志;请参见zip.FileHeader中的NonUFT8标志。还请参阅源代码中的注释。 - Martin Tournoij

-2
package main

import (
    "archive/zip"
    "fmt"
    "io"
    "os"
    "path/filepath"
    "strings"
)

func main() {
    var (
        Path = os.Args[1]
        Name = os.Args[2]
    )

    File, _ := os.Create(Name)
    PS := strings.Split(Path, "\\")
    PathName := strings.Join(PS[:len(PS)-1], "\\")
    os.Chdir(PathName)
    Path = PS[len(PS)-1]
    defer File.Close()
    Zip := zip.NewWriter(File)
    defer Zip.Close()
    walk := func(Path string, info os.FileInfo, err error) error {
        if err != nil {
            fmt.Println(err)
            return err
        }
        if info.IsDir() {
            return nil
        }
        Src, _ := os.Open(Path)
        defer Src.Close()
        //FileName, _ := Zip.Create(Path)
        h := &zip.FileHeader{Name: Path, Method: zip.Deflate, Flags: 0x800}
        FileName, _ := Zip.CreateHeader(h)
        io.Copy(FileName, Src)
        Zip.Flush()
        return nil
    }
    if err := filepath.Walk(Path, walk); err != nil {
        fmt.Println(err)
    }
}

如果您能添加一份解释来说明这如何解决问题,那将非常有帮助。 - Martin Tournoij

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接