Golang中jpeg.Decode解码多张图片失败问题探讨
Golang中jpeg.Decode解码多张图片失败问题探讨
我已成功在我的网页应用中实现了图片下载功能。图片在浏览器中显示正常;然而,当我尝试解码它们以进行尺寸调整时,却遇到了错误。GitHub上有一个关于此问题的帖子:
https://github.com/golang/go/issues/45902
该问题在那里尚未解决。对我来说,这个问题似乎在处理较大图片时更频繁地出现。这些图片在大多数应用程序中都能正常工作,例如:Affinity Photo 和 Mac Photos。我在 Affinity 中编辑了其中一个文件并减小了其尺寸。当我把宽度降到800像素后,我就能成功解码并调整其大小了。原图是4000像素宽,从 Mac Photos 导出的。解码失败并报错:unexpected EOF。这张照片是用 iPhone 12 拍摄的。我在大多数照片上都看到了这个错误。在处理了相当多的照片后,我发现只有当我把宽度降到大约800像素左右时,才能稳定地解码它们。在每一个解码失败的案例中,图片在浏览器中都能正常显示。
我尝试下载这张图片:
https://hiconsumption.com/fastest-cars-in-the-world/
它的宽度是1000像素,解码时遇到了同样的错误。
除非是我的代码哪里做错了,否则我觉得这个问题应该会被广泛报告才对。
var decodedImage image.Image
var err error
if fileType == "jpg" {
buf := bytes.NewBufferString(imageData)
reader := base64.NewDecoder(base64.StdEncoding, buf)
config, err := jpeg.DecodeConfig(reader)
if err != nil {
log.Println("error jpeg.DecodeConfig: ", err)
return err
} else {
log.Println("Width:", config.Width, "Height:", config.Height)
}
buf = bytes.NewBufferString(imageData)
reader = base64.NewDecoder(base64.StdEncoding, buf)
decodedImage, err = jpeg.Decode(reader)
if err != nil {
log.Println("jpeg.Decode failed error: ", err)
return err
}
}
更多关于Golang中jpeg.Decode解码多张图片失败问题探讨的实战教程也可以访问 https://www.itying.com/category-94-b0.html
哦,好的,那你推荐用什么替代呢?
更多关于Golang中jpeg.Decode解码多张图片失败问题探讨的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
没关系,我理解错了问题。我以为是浏览器无法读取你处理后的图像。
好的,没问题。上面的代码中出现了错误,具体在 jpeg.DecodeConfig() 和 jpeg.Decode() 处。
替换方案在此处描述 ioutil 包 - io/ioutil - pkg.go.dev
rschluet:
ioutil
很高兴你取得了一些进展!顺便提一下,ioutil 已被弃用。
我也不知道这一点!不过看起来修复起来很简单:ioutil.ReadAll 变成了 io.ReadAll,ioutil.ReadFile → os.ReadFile,等等。我刚才还担心了一下!
“如果请求体的尺寸尚未被 MaxBytesReader 限制,则其大小上限为 10MB。”
http package - net/http - pkg.go.dev
Package http provides HTTP client and server implementations.
除了隔离之外,我没有更好的建议。您能否将大图像下载为.jpg文件,并编写直接读取和解码该文件的Go代码?如果可以,您能否编写Go代码对下载的文件进行base-64编码,并将结果的大小与您在服务中接收到的数据大小进行比较?您可能在某个地方有截断请求数据的情况。
func main() {
fmt.Println("hello world")
}
感谢,了解限制是件好事,但我的图片都没有那么大。我目前正在处理的那张图片(在浏览器中显示正常)不到4MB。当它到达服务器时,大小为524288字节。这就解释了为什么当它被发送回浏览器时,只有大约顶部10%的部分显示出来。我没有使用MaxBytesReader,所以限制应该是10MB。我想知道浏览器是否限制了发送的数据量。我使用的是Mac上的Safari,还有iPad上的。
啊,好建议。在浏览器中,base64编码的图片显示正常。但是,如果我只是将该字符串保存到数据库,再取出来发送回浏览器,它就被截断了。我添加了代码来对从浏览器发送的数据进行校验和,它也因意外的EOF而失败。它以POST请求的形式到达服务器,我使用
request.ParseForm()
p.DataBodyMap = request.Form
来填充url.Values,它是一个map[string][]string。在浏览器提交和request.ParseForm()之间的某个环节,base64字符串被截断了。
是的——据我所知,没有计划进行破坏性变更。如果你查看 ioutil.ReadAll,它只是调用了现在更推荐的 io 版本:
// ReadAll reads from r until an error or EOF and returns the data it read.
// A successful call returns err == nil, not err == EOF. Because ReadAll is
// defined to read from src until EOF, it does not treat an EOF from Read
// as an error to be reported.
//
// As of Go 1.16, this function simply calls io.ReadAll.
func ReadAll(r io.Reader) ([]byte, error) {
return io.ReadAll(r)
}
根据文档(强调部分是我的):
自 Go 1.16 起,相同的功能现在由 io 包或 os 包提供,在新代码中应优先使用这些实现。详情请参阅具体函数的文档。
我在项目中遇到 ioutil 时会慢慢将其重构掉,但我并没有将其作为优先事项,因为没有迫切的需要(甚至没有非迫切的需要!只是轻微的偏好)。
好的,现在可以正常工作了。以下是代码:
case http.MethodPost:
contentType := request.Header.Get("Content-Type")
if strings.HasPrefix(contentType, "multipart/form-data") {
p.DataBodyMap = make(url.Values, 0)
request.ParseMultipartForm(1000)
for key, value := range request.MultipartForm.Value {
p.DataBodyMap[key] = value
}
photoFile, header, err := request.FormFile("photoBytesString")
if err == nil {
log.Println(header, err)
photoContentType := header.Header["Content-Type"][0]
log.Println(photoContentType)
photoBytes, _ := ioutil.ReadAll(photoFile)
log.Println("len(photoBytes): ", len(photoBytes))
encodedImage64 := base64.StdEncoding.EncodeToString(photoBytes)
encodedImage64WithPrefix := "data:" + photoContentType + ";base64," + encodedImage64
p.DataBodyMap["photoBytesString"] = []string{encodedImage64WithPrefix}
}
这段代码工作正常,没有截断任何数据。
啊,好建议。在浏览器中,base64编码的图片显示正常。但是,如果我只是将该字符串保存到数据库,再取出来发送回浏览器,它就被截断了。我添加了代码来对从浏览器发送的数据进行校验和,它也因意外的EOF而失败。它是作为POST请求到达服务器的,我使用了…
现在,当你说“将字符串保存到数据库”时,你指的是什么?你用的是哪种数据库?你试图保存base64编码图片的列的数据类型是什么?你确定问题出在请求上,而不是你的数据库里吗?
谢谢,很高兴知道限制是多少,但我的图片都没有那么大。我目前正在测试的图片(在浏览器中显示正常)不到4MB。当它到达服务器时,大小是524288字节。这就解释了为什么当它被发送回浏览器时,只显示了大约顶部10%的部分。我没有使用MaxBytesReader,所以限制应该是10MB。我在想是不是浏览器限制了发送的数据量。我使用的是Mac和iPad上的Safari。
我有一个处理图片上传/调整大小的生产应用程序,我们经常处理更大的图片。浏览器限制上传大小的可能性也极小。在我作为Web开发人员的这些年里,我遇到过很多奇怪的浏览器问题,但这对我来说是新的。
你能展示一下你的HTML表单以及服务器端处理上传的代码吗?你使用的是multipart/form-data吗?
这是一个已知的 JPEG 解码问题,主要出现在某些特定编码的 JPEG 图片上。问题通常与 JPEG 文件的渐进式编码或某些元数据格式有关。以下是解决方案和示例代码:
解决方案1:使用 image.Decode 替代 jpeg.Decode
import (
"bytes"
"encoding/base64"
"image"
_ "image/jpeg" // 注册JPEG解码器
"io"
)
func decodeImage(imageData string) (image.Image, error) {
buf := bytes.NewBufferString(imageData)
reader := base64.NewDecoder(base64.StdEncoding, buf)
// 先获取配置信息
config, format, err := image.DecodeConfig(reader)
if err != nil {
return nil, err
}
log.Printf("Format: %s, Width: %d, Height: %d", format, config.Width, config.Height)
// 重新创建reader进行完整解码
buf = bytes.NewBufferString(imageData)
reader = base64.NewDecoder(base64.StdEncoding, buf)
img, _, err := image.Decode(reader)
return img, err
}
解决方案2:使用 io.ReadAll 读取完整数据
import (
"bytes"
"encoding/base64"
"image"
"image/jpeg"
"io"
)
func decodeImageRobust(imageData string) (image.Image, error) {
// 解码base64
data, err := base64.StdEncoding.DecodeString(imageData)
if err != nil {
return nil, err
}
// 使用bytes.Reader而不是bytes.Buffer
reader := bytes.NewReader(data)
// 尝试解码配置
config, err := jpeg.DecodeConfig(reader)
if err != nil {
return nil, err
}
// 重置reader位置
reader.Seek(0, io.SeekStart)
// 解码完整图片
return jpeg.Decode(reader)
}
解决方案3:添加错误恢复机制
import (
"bytes"
"encoding/base64"
"image"
"image/jpeg"
"io"
)
func decodeImageWithRetry(imageData string, maxRetries int) (image.Image, error) {
data, err := base64.StdEncoding.DecodeString(imageData)
if err != nil {
return nil, err
}
var img image.Image
var decodeErr error
for i := 0; i < maxRetries; i++ {
reader := bytes.NewReader(data)
img, decodeErr = jpeg.Decode(reader)
if decodeErr == nil {
return img, nil
}
// 如果是EOF错误,尝试不同的方法
if decodeErr == io.EOF || decodeErr == io.ErrUnexpectedEOF {
// 尝试使用image.Decode
reader.Seek(0, io.SeekStart)
img, _, decodeErr = image.Decode(reader)
if decodeErr == nil {
return img, nil
}
}
}
return nil, decodeErr
}
解决方案4:使用第三方库处理问题图片
import (
"bytes"
"encoding/base64"
"image"
"github.com/disintegration/imaging" // 第三方图像处理库
)
func decodeWithImaging(imageData string) (image.Image, error) {
data, err := base64.StdEncoding.DecodeString(imageData)
if err != nil {
return nil, err
}
// 使用imaging库解码,它内部有更好的错误处理
img, err := imaging.Decode(bytes.NewReader(data))
if err != nil {
return nil, err
}
return img, nil
}
关键点说明:
image.Decode比jpeg.Decode更健壮,因为它会尝试所有注册的解码器- 使用
bytes.Reader而不是bytes.Buffer可以更好地控制读取位置 - 某些JPEG文件可能包含损坏的元数据或使用非标准的编码方式
- 渐进式JPEG在某些情况下可能导致解码问题
对于你的具体代码,建议修改为:
var decodedImage image.Image
var err error
if fileType == "jpg" {
// 解码base64数据
data, err := base64.StdEncoding.DecodeString(imageData)
if err != nil {
log.Println("base64 decode error: ", err)
return err
}
reader := bytes.NewReader(data)
// 使用image.DecodeConfig替代jpeg.DecodeConfig
config, format, err := image.DecodeConfig(reader)
if err != nil {
log.Println("image.DecodeConfig error: ", err)
return err
}
log.Printf("Format: %s, Width: %d, Height: %d", format, config.Width, config.Height)
// 重置reader并解码
reader.Seek(0, io.SeekStart)
decodedImage, _, err = image.Decode(reader)
if err != nil {
log.Println("image.Decode failed error: ", err)
return err
}
}
这个问题确实在Go社区中被多次报告,主要影响某些特定编码的JPEG文件。使用上述方法可以显著提高解码成功率。


