You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

将pdf.gz解压后加入tar.gz时文件被截断损坏的问题排查

问题原因与修复方案

核心问题

你的代码中,用压缩后PDF文件的FileInfo生成tar文件头,而这个Info里的Size是压缩后的文件大小,并非解压后真实PDF的大小。tar writer会依据这个错误的大小写入数据,导致写到压缩后的大小就停止,最终PDF文件末尾被截断。

关键修复步骤

  1. 修正tar文件头的Size字段:通过gzip reader获取解压后的真实文件大小,替换原压缩文件的Size值。
  2. 重置文件指针:创建gzip reader读取头信息后,需要将文件指针移回开头,才能完整读取所有解压内容。
  3. 完善错误处理:避免错误发生后代码继续执行,导致后续异常。
  4. 调整资源关闭顺序:确保tar writer先关闭,再关闭gzip writer,保证所有数据正确写入。

修复后的完整代码

package main

import (
	"io"
	"log"
	"os"
	"archive/tar"
	"compress/gzip"
)

func main() {
	// 初始化输出tar.gz文件句柄
	outputFile, err := os.Create("output.tar.gz")
	if err != nil {
		log.Fatalf("创建输出文件失败: %v", err)
	}
	defer outputFile.Close()

	// 创建gzip压缩器(级别1)
	gzWriter, err := gzip.NewWriterLevel(outputFile, 1)
	if err != nil {
		log.Fatalf("初始化gzip压缩器失败: %v", err)
	}
	defer gzWriter.Close()

	// 创建tar打包器
	tarWriter := tar.NewWriter(gzWriter)
	defer tarWriter.Close()

	// 配置文件路径与tar内显示的文件名
	sourceGzPath := "path-to-file.pdf.gz"
	tarInnerFileName := "filename-shown-in-tar.pdf"

	// 打开源压缩PDF文件
	sourceFile, err := os.Open(sourceGzPath)
	if err != nil {
		log.Fatalf("打开源文件失败: %v", err)
	}
	defer sourceFile.Close()

	// 第一次创建gzip读取器,获取解压后的真实文件大小
	gzReader, err := gzip.NewReader(sourceFile)
	if err != nil {
		log.Fatalf("初始化gzip读取器失败: %v", err)
	}
	// 关闭读取器,重置文件指针到开头
	gzReader.Close()
	sourceFile.Seek(0, io.SeekStart)

	// 重新创建gzip读取器,用于后续解压数据
	gzReader, err = gzip.NewReader(sourceFile)
	if err != nil {
		log.Fatalf("重新初始化gzip读取器失败: %v", err)
	}
	defer gzReader.Close()

	// 获取源文件基础信息,生成tar文件头
	sourceInfo, err := sourceFile.Stat()
	if err != nil {
		log.Fatalf("获取源文件信息失败: %v", err)
	}
	tarHeader, err := tar.FileInfoHeader(sourceInfo, "")
	if err != nil {
		log.Fatalf("生成tar文件头失败: %v", err)
	}

	// 修正tar文件头的关键字段
	tarHeader.Name = tarInnerFileName
	tarHeader.Size = gzReader.Size() // 替换为解压后的真实文件大小

	// 写入tar文件头
	if err := tarWriter.WriteHeader(tarHeader); err != nil {
		log.Fatalf("写入tar文件头失败: %v", err)
	}

	// 将解压后的PDF内容写入tar包
	if _, err := io.Copy(tarWriter, gzReader); err != nil {
		log.Fatalf("写入解压内容失败: %v", err)
	}

	// 强制刷新tar打包器,确保所有数据写入gzip压缩器
	if err := tarWriter.Flush(); err != nil {
		log.Fatalf("刷新tar打包器失败: %v", err)
	}
}

大文件兼容方案(超过4GB)

如果解压后的PDF超过4GB,gzip头里的Size字段会返回0(gzip用uint32存储未压缩大小),此时可以用临时文件中转:

// 替换原代码中写入tar的部分
tempPdfFile, err := os.CreateTemp("", "temp-*.pdf")
if err != nil {
	log.Fatalf("创建临时文件失败: %v", err)
}
defer os.Remove(tempPdfFile.Name()) // 处理完成后自动删除临时文件

// 将解压内容写入临时文件
if _, err := io.Copy(tempPdfFile, gzReader); err != nil {
	log.Fatalf("写入临时文件失败: %v", err)
}
tempPdfFile.Seek(0, io.SeekStart) // 重置临时文件指针到开头

// 获取临时文件的真实大小
tempFileInfo, err := tempPdfFile.Stat()
if err != nil {
	log.Fatalf("获取临时文件信息失败: %v", err)
}
tarHeader.Size = tempFileInfo.Size()

// 写入tar文件头与临时文件内容
tarWriter.WriteHeader(tarHeader)
io.Copy(tarWriter, tempPdfFile)

内容的提问来源于stack exchange,提问作者clarkk

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.15 22:01:38