使用Go的存档/ zip创建包含Unicode文件名的zip存档

时间:2015-05-04 08:49:56

标签: string go unicode zip

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

这个mydir路径:

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

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

2 个答案:

答案 0 :(得分:9)

问题是默认情况下,在zip条目名称中Zip specification只允许使用ASCII字符,更具体地说:(来源:APPENDIX D

  

附录D.1 ZIP格式历来仅支持原始IBM PC角色   编码集,通常称为IBM代码页437.这限制了存储   文件名字符仅限于原始MS-DOS值范围内的字符   并且不能正确支持其他字符编码中的文件名,或   语言。为了解决这个限制,本规范将支持   跟随变化。

后来添加了对Unicode名称的支持。这可以使用称为general purpose bit 11的特殊位标记,也称为Language encoding flag (EFS)

  

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

     

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

Go出现并支持general purpose bit flag:它是FileHeader结构的Flags字段。遗憾的是Go没有设置此位的方法,默认情况下为0。

因此,添加对Unicode名称的支持的最简单方法是将bit 11设置为1。而不是

FileName, _ := Zip.Create(Path)

使用以下命令启动您的zip条目:

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

第一行创建FileHeader,其中为0x800字段设置bit 11Flags)值,告知文件名将使用{{ 1}}(这是Go将UTF-8写入string时的行为。)

注意:

通过这样做,将保留UTF-8文件名,但并非所有zip阅读器/提取器都支持它。例如,在Windows上,Windows文件处理程序,Windows资源管理器不会将其解码为UTF-8,但是例如更严重的Zip处理程序(例如SecureZip)将看到UTF-8文件名并将提取文件名正确(使用UTF-8解码)。

答案 1 :(得分:-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)
    }
}