Go로 파일 zip을 압축할 때 시간 스탬프가 빗나가는 문제를 회피하는 정책

16270 단어 Go

개시하다


파일과 zip을 Go로 압축하는 방법으로 문제와 회피의 문장을 수반한다.
(더 좋은 방법이 있다면 지적해 주세요.)

실험 환경


go version go1.4.2 darwin/amd64
(Mac OS X)

zip 압축 코드


파일의 zip 압축 코드
//ファイルをzip圧縮します。
//zipPathは、zip上で表現されるパスを指定します。
//targetFileは、圧縮するファイルシステム上のファイルのパスを指定します。

func ZipFile(writer *zip.Writer, zipPath string, targetFile string) error {
    f, err := os.Open(targetFile)
    if err != nil {
        return err
    }
    defer f.Close()

    body, err := ioutil.ReadAll(f)

    if err != nil {
        return err
    }

    info , err := os.Stat(targetFile)
    if err != nil {
        return err
    }

    header, _ := zip.FileInfoHeader(info)
    //zip用のパスを設定
    //これを設定しないと、zip内でディレクトリの中に作られない。
    header.Name = zipPath

    zf, err := writer.CreateHeader(header)
    if err != nil {
        return err
    }

    if _, err := zf.Write(body); err != nil {
        return err
    }
    return nil
}
파일 zip 압축 호출 사이드 코드
buf := new(bytes.Buffer)
zw := zip.NewWriter(buf)

if ferr := ZipFile(zw, "zip_path", "file.txt"); ferr != nil {
    panic(ferr)
}

if ferr := zw.Close(); ferr != nil {
    panic(ferr)
}

보통 쓰면 이런 느낌일 거예요.스케줄러:맞지?
참조: http://golang.org/pkg/archive/zip/

실험


이걸로 압축할게요.
보기 흉하다
왼쪽은 원본 파일이고, 오른쪽은 압축된 파일의 시간 스탬프입니다.

시간 스탬프가 다른 거 알아요.
(그리고 마침 9시간 늦었다.)
즉, 위에서 말한 바와 같이, zip.FileInfoHeader를 사용하면 파일 시간이 달라집니다.

원인의 예측


예상일 뿐이지만 탐색해 보자
zip.FileInfoHeader->FileHeader.SetModTime->timeToMsDosTime
http://golang.org/src/archive/zip/struct.go#L170
t = t.In(time.UTC)
그러나 원본 파일의 시간 스탬프를 표준 시간으로 처리하면 JST와의 차이(9시간)가 늦어진다. 시간TomsDoosTime의 처리가 느리게 계산되기 때문이다.

대응 코드


이거 zip.나는 FileInfoHeader가 문제라고 생각하는데, 밖에서는 아무리 해도 할 수 없을 것 같다.
객체 코드
http://golang.org/src/archive/zip/struct.go#L110
그냥 복사해서 살짝 수정해 주세요.
FileInfoHeader 수정
func FileInfoHeader(fi os.FileInfo) (*zip.FileHeader, error) {
    size := fi.Size()
    fh := &zip.FileHeader{
        Name:               fi.Name(),
        UncompressedSize64: uint64(size),
    }

    // 現在のLocalを取得する。 
    local := time.Now().Local()

    //時刻のoffset(秒)を取得する。
    _, offset := local.Zone()

    //ファイルスタンプの時間に時差分を追加する。
    fh.SetModTime(fi.ModTime().Add(time.Duration(offset) * time.Second))
    fh.SetMode(fi.Mode())
    var uintmax = uint32((1 << 32) - 1)
    if fh.UncompressedSize64 > uint64(uintmax) {
        fh.UncompressedSize = uintmax
    } else {
        fh.UncompressedSize = uint32(fh.UncompressedSize64)
    }
    return fh, nil
}
주석 부분은 개조 부분이다.
로컬 환경의 시차 편이를 가져와 파일의 시간 스탬프에 추가하면 됩니다.
이렇게 되면, zip.FileInfoHeader를 위의 FileInfoHeader로 교체합니다.
첫 번째 코드 변경점
    header, _ := FileInfoHeader(info)
    //zip用のパスを設定
    //これを設定しないと、zip内でディレクトリの中に作られない。
    header.Name = zipPath

총결산


zip.FileInfoHeader를 사용하는 동안 잘못된 타임스탬프가 처리되었습니다.
zip.FileInfoHeader(time Toms Doostime)의 버그 같지만, (없을 것 같아) 자신의 잘못된 인식인가요?


지정한 디렉터리를 압축하는 데 사용되는 코드입니다.
디렉터리의 zip 압축 코드
//zipPathは、zip上で表現されるパスを指定します。
//targetDirは、圧縮するファイルシステム上のディレクトリのパスを指定します。
func ZipDir(writer *zip.Writer, zipPath string, targetDir string) error {
    //圧縮するディレクトリをオープンします。
    dir, err := os.Open(targetDir)
    if err != nil {
        return err
    }
    defer dir.Close()

    //ディレクトリ内のファイル一覧を取得します。
    files, err := dir.Readdirnames(-1)
    if err != nil {
        return err
    }

    for _, fname := range files {
        if err := ZipFile(writer, zipPath+"/"+fname, targetDir+"/"+fname); err != nil {
            return err
        }
    }
    return nil
}
디렉터리 zip 압축 호출 사이드 코드
buf := new(bytes.Buffer)
zw := zip.NewWriter(buf)

if ferr := ZipDir(zw, "zip_dir", "dir"); ferr != nil {
    panic(ferr)
}

if ferr := zw.Close(); ferr != nil {
    panic(ferr)
}

참고 자료


고오 언어로 무압축 zip을 만들고 싶어요.

좋은 웹페이지 즐겨찾기