Golang archive/zip生成的zip文件损坏。

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

Golang archive/zip producing corrupt zip files

问题

我用Go语言编写了一个小工具来压缩文件夹。在许多情况下,它似乎工作正常,但偶尔会生成一个在解压缩应用程序中打开时被识别为损坏的zip文件(它们似乎都对此抱怨)。

以下是代码:

  1. const (
  2. singleFileByteLimit = 107374182400 // 1 GB
  3. chunkSize = 1024 // 1 KB
  4. )
  5. // ZipFolder将给定的文件夹压缩为指定名称的zip文件
  6. func ZipFolder(srcFolder string, destFile string) error {
  7. z := &zipper{
  8. srcFolder: srcFolder,
  9. destFile: destFile,
  10. }
  11. return z.zipFolder()
  12. }
  13. // 我们需要一个内部结构体,因为filepath WalkFunc
  14. // 不允许自定义参数。所以我们在这里保存它们,以便
  15. // 它可以访问它们
  16. type zipper struct {
  17. srcFolder string
  18. destFile string
  19. writer *zip.Writer
  20. }
  21. // 内部函数用于压缩文件夹
  22. func (z *zipper) zipFolder() error {
  23. // 创建zip文件
  24. zipFile, err := os.Create(z.destFile)
  25. if err != nil {
  26. return err
  27. }
  28. defer zipFile.Close()
  29. // 创建zip写入器
  30. z.writer = zip.NewWriter(zipFile)
  31. // 遍历源文件夹
  32. err = filepath.Walk(z.srcFolder, z.zipFile)
  33. if err != nil {
  34. return nil
  35. }
  36. // 关闭zip文件
  37. err = z.writer.Close()
  38. if err != nil {
  39. return err
  40. }
  41. return nil
  42. }
  43. // 内部函数用于压缩文件,由filepath.Walk在每个文件上调用
  44. func (z *zipper) zipFile(path string, f os.FileInfo, err error) error {
  45. // 只压缩文件(目录由其中的文件创建)
  46. // TODO:允许在没有文件的情况下创建文件夹
  47. if !f.IsDir() && f.Size() > 0 {
  48. // 打开文件
  49. file, err := os.Open(path)
  50. if err != nil {
  51. return err
  52. }
  53. defer file.Close()
  54. // 在zip中创建新文件
  55. fileName := strings.TrimPrefix(path, z.srcFolder+"/")
  56. w, err := z.writer.Create(fileName)
  57. if err != nil {
  58. return err
  59. }
  60. // 将文件内容复制到zip写入器中
  61. err = copyContents(file, w)
  62. if err != nil {
  63. return err
  64. }
  65. }
  66. return nil
  67. }
  68. func copyContents(r io.Reader, w io.Writer) error {
  69. var size int64
  70. for {
  71. b := make([]byte, chunkSize)
  72. // 为了避免zip炸弹,我们限制文件大小
  73. size += chunkSize
  74. if size > singleFileByteLimit {
  75. return errors.New("文件太大,请联系我们寻求帮助")
  76. }
  77. // 将块读入内存
  78. length, err := r.Read(b)
  79. if err == io.EOF {
  80. break
  81. } else if err != nil {
  82. return err
  83. }
  84. // 将块写入zip文件
  85. _, err = w.Write(b[:length])
  86. if err != nil {
  87. return err
  88. }
  89. }
  90. return nil
  91. }

希望这可以帮助到你。

英文:

I wrote a small utility in Go to zip a folder. It seems to work in many cases, but every now and then it produces a zip file that is coming up as corrupt when I open it in an unzip app (they all seem to complain about it).

Here is the code:

  1. const (
  2. singleFileByteLimit = 107374182400 // 1 GB
  3. chunkSize = 1024 // 1 KB
  4. )
  5. // ZipFolder zips the given folder to the a zip file
  6. // with the given name
  7. func ZipFolder(srcFolder string, destFile string) error {
  8. z := &zipper{
  9. srcFolder: srcFolder,
  10. destFile: destFile,
  11. }
  12. return z.zipFolder()
  13. }
  14. // We need a struct internally because the filepath WalkFunc
  15. // doesn't allow custom params. So we save them here so it can
  16. // access them
  17. type zipper struct {
  18. srcFolder string
  19. destFile string
  20. writer *zip.Writer
  21. }
  22. // internal function to zip a folder
  23. func (z *zipper) zipFolder() error {
  24. // create zip file
  25. zipFile, err := os.Create(z.destFile)
  26. if err != nil {
  27. return err
  28. }
  29. defer zipFile.Close()
  30. // create zip writer
  31. z.writer = zip.NewWriter(zipFile)
  32. // traverse the source folder
  33. err = filepath.Walk(z.srcFolder, z.zipFile)
  34. if err != nil {
  35. return nil
  36. }
  37. // close the zip file
  38. err = z.writer.Close()
  39. if err != nil {
  40. return err
  41. }
  42. return nil
  43. }
  44. // internal function to zip a file, called by filepath.Walk on each file
  45. func (z *zipper) zipFile(path string, f os.FileInfo, err error) error {
  46. // only zip files (directories are created by the files inside of them)
  47. // TODO allow creating folder when no files are inside
  48. if !f.IsDir() && f.Size() > 0 {
  49. // open file
  50. file, err := os.Open(path)
  51. if err != nil {
  52. return err
  53. }
  54. defer file.Close()
  55. // create new file in zip
  56. fileName := strings.TrimPrefix(path, z.srcFolder+"/")
  57. w, err := z.writer.Create(fileName)
  58. if err != nil {
  59. return err
  60. }
  61. // copy contents of the file to the zip writer
  62. err = copyContents(file, w)
  63. if err != nil {
  64. return err
  65. }
  66. }
  67. return nil
  68. }
  69. func copyContents(r io.Reader, w io.Writer) error {
  70. var size int64
  71. for {
  72. b := make([]byte, chunkSize)
  73. // we limit the size to avoid zip bombs
  74. size += chunkSize
  75. if size > singleFileByteLimit {
  76. return errors.New("file too large, please contact us for assitance")
  77. }
  78. // read chunk into memory
  79. length, err := r.Read(b)
  80. if err == io.EOF {
  81. break
  82. } else if err != nil {
  83. return err
  84. }
  85. // write chunk to zip file
  86. _, err = w.Write(b[:length])
  87. if err != nil {
  88. return err
  89. }
  90. }
  91. return nil
  92. }

答案1

得分: 3

阅读了你的代码后,我修复了一些看起来不正确的地方。请尝试以下代码:

  1. const (
  2. singleFileByteLimit = 107374182400 // 1 GB
  3. chunkSize = 4096 // 4 KB
  4. )
  5. func copyContents(r io.Reader, w io.Writer) error {
  6. var size int64
  7. b := make([]byte, chunkSize)
  8. for {
  9. // 为了避免压缩炸弹,我们限制文件大小
  10. size += chunkSize
  11. if size > singleFileByteLimit {
  12. return errors.New("文件太大,请联系我们寻求帮助")
  13. }
  14. // 将块读入内存
  15. length, err := r.Read(b[:cap(b)])
  16. if err != nil {
  17. if err != io.EOF {
  18. return err
  19. }
  20. if length == 0 {
  21. break
  22. }
  23. }
  24. // 将块写入 zip 文件
  25. _, err = w.Write(b[:length])
  26. if err != nil {
  27. return err
  28. }
  29. }
  30. return nil
  31. }
  32. // 我们需要一个内部结构体,因为 filepath.WalkFunc 不允许自定义参数。所以我们在这里保存它们以便访问
  33. type zipper struct {
  34. srcFolder string
  35. destFile string
  36. writer *zip.Writer
  37. }
  38. // 内部函数,用于在每个文件上调用 filepath.Walk 以进行压缩
  39. func (z *zipper) zipFile(path string, f os.FileInfo, err error) error {
  40. if err != nil {
  41. return err
  42. }
  43. // 只压缩文件(目录由其中的文件创建)
  44. // TODO:允许在没有文件的情况下创建文件夹
  45. if !f.Mode().IsRegular() || f.Size() == 0 {
  46. return nil
  47. }
  48. // 打开文件
  49. file, err := os.Open(path)
  50. if err != nil {
  51. return err
  52. }
  53. defer file.Close()
  54. // 在 zip 中创建新文件
  55. fileName := strings.TrimPrefix(path, z.srcFolder+"/")
  56. w, err := z.writer.Create(fileName)
  57. if err != nil {
  58. return err
  59. }
  60. // 将文件内容复制到 zip writer
  61. err = copyContents(file, w)
  62. if err != nil {
  63. return err
  64. }
  65. return nil
  66. }
  67. // 内部函数,用于压缩文件夹
  68. func (z *zipper) zipFolder() error {
  69. // 创建 zip 文件
  70. zipFile, err := os.Create(z.destFile)
  71. if err != nil {
  72. return err
  73. }
  74. defer zipFile.Close()
  75. // 创建 zip writer
  76. z.writer = zip.NewWriter(zipFile)
  77. err = filepath.Walk(z.srcFolder, z.zipFile)
  78. if err != nil {
  79. return nil
  80. }
  81. // 关闭 zip 文件
  82. err = z.writer.Close()
  83. if err != nil {
  84. return err
  85. }
  86. return nil
  87. }
  88. // ZipFolder 将给定的文件夹压缩为指定名称的 zip 文件
  89. func ZipFolder(srcFolder string, destFile string) error {
  90. z := &zipper{
  91. srcFolder: srcFolder,
  92. destFile: destFile,
  93. }
  94. return z.zipFolder()
  95. }

这段代码实现了将指定文件夹压缩为 zip 文件的功能。你可以调用 ZipFolder 函数,并传入源文件夹路径和目标 zip 文件路径来实现压缩。

英文:

Reading through your code, I fixed things that didn't look right. Try the following:

  1. const (
  2. singleFileByteLimit = 107374182400 // 1 GB
  3. chunkSize = 4096 // 4 KB
  4. )
  5. func copyContents(r io.Reader, w io.Writer) error {
  6. var size int64
  7. b := make([]byte, chunkSize)
  8. for {
  9. // we limit the size to avoid zip bombs
  10. size += chunkSize
  11. if size > singleFileByteLimit {
  12. return errors.New("file too large, please contact us for assistance")
  13. }
  14. // read chunk into memory
  15. length, err := r.Read(b[:cap(b)])
  16. if err != nil {
  17. if err != io.EOF {
  18. return err
  19. }
  20. if length == 0 {
  21. break
  22. }
  23. }
  24. // write chunk to zip file
  25. _, err = w.Write(b[:length])
  26. if err != nil {
  27. return err
  28. }
  29. }
  30. return nil
  31. }
  32. // We need a struct internally because the filepath WalkFunc
  33. // doesn't allow custom params. So we save them here so it can
  34. // access them
  35. type zipper struct {
  36. srcFolder string
  37. destFile string
  38. writer *zip.Writer
  39. }
  40. // internal function to zip a file, called by filepath.Walk on each file
  41. func (z *zipper) zipFile(path string, f os.FileInfo, err error) error {
  42. if err != nil {
  43. return err
  44. }
  45. // only zip files (directories are created by the files inside of them)
  46. // TODO allow creating folder when no files are inside
  47. if !f.Mode().IsRegular() || f.Size() == 0 {
  48. return nil
  49. }
  50. // open file
  51. file, err := os.Open(path)
  52. if err != nil {
  53. return err
  54. }
  55. defer file.Close()
  56. // create new file in zip
  57. fileName := strings.TrimPrefix(path, z.srcFolder+"/")
  58. w, err := z.writer.Create(fileName)
  59. if err != nil {
  60. return err
  61. }
  62. // copy contents of the file to the zip writer
  63. err = copyContents(file, w)
  64. if err != nil {
  65. return err
  66. }
  67. return nil
  68. }
  69. // internal function to zip a folder
  70. func (z *zipper) zipFolder() error {
  71. // create zip file
  72. zipFile, err := os.Create(z.destFile)
  73. if err != nil {
  74. return err
  75. }
  76. defer zipFile.Close()
  77. // create zip writer
  78. z.writer = zip.NewWriter(zipFile)
  79. err = filepath.Walk(z.srcFolder, z.zipFile)
  80. if err != nil {
  81. return nil
  82. }
  83. // close the zip file
  84. err = z.writer.Close()
  85. if err != nil {
  86. return err
  87. }
  88. return nil
  89. }
  90. // ZipFolder zips the given folder to the a zip file
  91. // with the given name
  92. func ZipFolder(srcFolder string, destFile string) error {
  93. z := &zipper{
  94. srcFolder: srcFolder,
  95. destFile: destFile,
  96. }
  97. return z.zipFolder()
  98. }

huangapple
  • 本文由 发表于 2014年7月17日 04:59:11
  • 转载请务必保留本文链接:https://go.coder-hub.com/24790635.html
匿名

发表评论

匿名网友

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

确定