2014-07-16 5 views
2

Go에서 작은 유틸리티를 작성하여 폴더를 압축하십시오. 그것은 많은 경우에 작동하는 것처럼 보이지만, 지금은 압축 해제 앱에서 열 때 손상 될 것으로 보이는 zip 파일을 생성합니다 (모든 파일이 불만을 나타냅니다). 여기 Golang 아카이브/압축 손상된 zip 파일 생성

코드입니다 : 당신의 코드를 읽기

const (
    singleFileByteLimit = 107374182400 // 1 GB 
    chunkSize   = 1024   // 1 KB 
) 

// ZipFolder zips the given folder to the a zip file 
// with the given name 
func ZipFolder(srcFolder string, destFile string) error { 
    z := &zipper{ 
     srcFolder: srcFolder, 
     destFile: destFile, 
    } 
    return z.zipFolder() 
} 

// We need a struct internally because the filepath WalkFunc 
// doesn't allow custom params. So we save them here so it can 
// access them 
type zipper struct { 
    srcFolder string 
    destFile string 
    writer *zip.Writer 
} 

// internal function to zip a folder 
func (z *zipper) zipFolder() error { 
    // create zip file 
    zipFile, err := os.Create(z.destFile) 
    if err != nil { 
     return err 
    } 
    defer zipFile.Close() 

    // create zip writer 
    z.writer = zip.NewWriter(zipFile) 

    // traverse the source folder 
    err = filepath.Walk(z.srcFolder, z.zipFile) 
    if err != nil { 
     return nil 
    } 

    // close the zip file 
    err = z.writer.Close() 
    if err != nil { 
     return err 
    } 
    return nil 
} 

// internal function to zip a file, called by filepath.Walk on each file 
func (z *zipper) zipFile(path string, f os.FileInfo, err error) error { 
    // only zip files (directories are created by the files inside of them) 
    // TODO allow creating folder when no files are inside 
    if !f.IsDir() && f.Size() > 0 { 
     // open file 
     file, err := os.Open(path) 
     if err != nil { 
      return err 
     } 
     defer file.Close() 

     // create new file in zip 
     fileName := strings.TrimPrefix(path, z.srcFolder+"/") 
     w, err := z.writer.Create(fileName) 
     if err != nil { 
      return err 
     } 

     // copy contents of the file to the zip writer 
     err = copyContents(file, w) 
     if err != nil { 
      return err 
     } 
    } 

    return nil 
} 

func copyContents(r io.Reader, w io.Writer) error { 
    var size int64 
    for { 
     b := make([]byte, chunkSize) 

     // we limit the size to avoid zip bombs 
     size += chunkSize 
     if size > singleFileByteLimit { 
      return errors.New("file too large, please contact us for assitance") 
     } 

     // read chunk into memory 
     length, err := r.Read(b) 
     if err == io.EOF { 
      break 
     } else if err != nil { 
      return err 
     } 
     // write chunk to zip file 
     _, err = w.Write(b[:length]) 
     if err != nil { 
      return err 
     } 
    } 
    return nil 
} 
+3

손상된 우편 번호를 입력 할 수 있습니까? – Kluyg

+0

왜 copyContents를 작성하고 io.Copy를 사용하지 않아야합니까? – mlbright

+0

이 작업을 수행하는 Go 패키지가 있습니다. https://github.com/jhoonb/archivex –

답변

4

, 나는 잘 보이지 않았다 일을 해결했습니다. 다음을 시도해보십시오.

const (
    singleFileByteLimit = 107374182400 // 1 GB 
    chunkSize   = 4096   // 4 KB 
) 

func copyContents(r io.Reader, w io.Writer) error { 
    var size int64 
    b := make([]byte, chunkSize) 
    for { 
     // we limit the size to avoid zip bombs 
     size += chunkSize 
     if size > singleFileByteLimit { 
      return errors.New("file too large, please contact us for assistance") 
     } 
     // read chunk into memory 
     length, err := r.Read(b[:cap(b)]) 
     if err != nil { 
      if err != io.EOF { 
       return err 
      } 
      if length == 0 { 
       break 
      } 
     } 
     // write chunk to zip file 
     _, err = w.Write(b[:length]) 
     if err != nil { 
      return err 
     } 
    } 
    return nil 
} 

// We need a struct internally because the filepath WalkFunc 
// doesn't allow custom params. So we save them here so it can 
// access them 
type zipper struct { 
    srcFolder string 
    destFile string 
    writer *zip.Writer 
} 

// internal function to zip a file, called by filepath.Walk on each file 
func (z *zipper) zipFile(path string, f os.FileInfo, err error) error { 
    if err != nil { 
     return err 
    } 
    // only zip files (directories are created by the files inside of them) 
    // TODO allow creating folder when no files are inside 
    if !f.Mode().IsRegular() || f.Size() == 0 { 
     return nil 
    } 
    // open file 
    file, err := os.Open(path) 
    if err != nil { 
     return err 
    } 
    defer file.Close() 
    // create new file in zip 
    fileName := strings.TrimPrefix(path, z.srcFolder+"/") 
    w, err := z.writer.Create(fileName) 
    if err != nil { 
     return err 
    } 
    // copy contents of the file to the zip writer 
    err = copyContents(file, w) 
    if err != nil { 
     return err 
    } 
    return nil 
} 

// internal function to zip a folder 
func (z *zipper) zipFolder() error { 
    // create zip file 
    zipFile, err := os.Create(z.destFile) 
    if err != nil { 
     return err 
    } 
    defer zipFile.Close() 
    // create zip writer 
    z.writer = zip.NewWriter(zipFile) 
    err = filepath.Walk(z.srcFolder, z.zipFile) 
    if err != nil { 
     return nil 
    } 
    // close the zip file 
    err = z.writer.Close() 
    if err != nil { 
     return err 
    } 
    return nil 
} 

// ZipFolder zips the given folder to the a zip file 
// with the given name 
func ZipFolder(srcFolder string, destFile string) error { 
    z := &zipper{ 
     srcFolder: srcFolder, 
     destFile: destFile, 
    } 
    return z.zipFolder() 
} 
+9

비슷한 문제로이 게시물에 걸려 넘어 지거나 미래의 독자 및 OP를 위해 변경 한 사항에 대해 설명했다면 유용 할 것입니다. – fuz

+0

감사합니다. 그것은 작동합니다. 코드를 단축하려면 copyContents를 아무런 변경없이 _, err = io.Copy (w, file)로 바꿀 수 있습니다. – Oleg

관련 문제