PHP无法通过Golang解压缩gzip数据。

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

PHP can't decompress gzip data by Golang

问题

为什么我的 PHP 示例中无法使用 Go 解压缩 gzip 数据,但是 PHP 将 gzip 数据发送到 Go 是成功的?我需要将 gzip JSON 数据从 Go 发送到 PHP API 服务。

测试结果

 -> |  php   |  go 
---------------------
php |  成功  |  成功
go  |  失败  |  成功

PHP 代码

class GzipDemo
{
    public function gzen($data, $file){
       $json_data = json_encode($data);
       $gz_data = gzencode($json_data,9);
       file_put_contents($file,$gz_data);
    }

    public function gzdn($file){
       $data = file_get_contents($file);
        $unpacked = gzdecode($data);
        if ($unpacked === FALSE)
        {
            print("失败:".$file."\n");
        }else{
             print($file. " 结果数据 :".$unpacked ."\n");
        }
    }
}

$demo = new GzipDemo();
$file="phpgzip.txt";
$demo->gzen("data",$file);
$demo->gzdn($file);
$demo->gzdn("gogzip.txt");

这个结果:
PHP 到 PHP 正常。
Go 到 PHP 失败。

Go 代码

package main

import (
	"bytes"
	"compress/gzip"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"os"
)

func main() {
	gzen("data", "gogzip.txt")
	gden("gogzip.txt")
	gden("phpgzip.txt")
}
func gzen(data string, file string) {
	b, _ := json.Marshal(data)
	buffer := new(bytes.Buffer)
	w, _ := gzip.NewWriterLevel(buffer, gzip.BestCompression)
	defer w.Close()
	w.Write(b)
	w.Flush()
	ioutil.WriteFile(file, buffer.Bytes(), os.ModePerm)
}
func gden(file string) {
	b, _ := ioutil.ReadFile(file)
	buffer := new(bytes.Buffer)
	buffer.Write(b)
	r, _ := gzip.NewReader(buffer)
    defer r.close()
	data, _ := ioutil.ReadAll(r)
	fmt.Println(file, " 结果数据:", string(data))
}

这个结果:
Go 到 Go 正常。
PHP 到 Go 正常。

英文:

Why can't decompress gzip data by Go in my PHP demo, but PHP gzip data to Go is successful? I need post gzip JSON data from Go to PHP API service.

Test result

 -> |  php   |  go 
---------------------
php |  ok    |  ok
go  |  fail  |  ok

PHP code

class GzipDemo
{
    public function gzen($data, $file){
       $json_data = json_encode($data);
       $gz_data = gzencode($json_data,9);
       file_put_contents($file,$gz_data);
    }

    public function gzdn($file){
       $data = file_get_contents($file);
        $unpacked = gzdecode($data);
        if ($unpacked === FALSE)
        {
            print("failed:".$file."\n");
        }else{
             print($file. " result Data :".$unpacked ."\n");
        }
    }
}

$demo = new GzipDemo();
$file="phpgzip.txt";
$demo->gzen("data",$file);
$demo->gzdn($file);
$demo->gzdn("gogzip.txt");

This result:
PHP to PHP okay.
Go to PHP fail.

Go code

package main

import (
	"bytes"
	"compress/gzip"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"os"
)

func main() {
	gzen("data", "gogzip.txt")
	gden("gogzip.txt")
	gden("phpgzip.txt")
}
func gzen(data string, file string) {
	b, _ := json.Marshal(data)
	buffer := new(bytes.Buffer)
	w, _ := gzip.NewWriterLevel(buffer, gzip.BestCompression)
	defer w.Close()
	w.Write(b)
	w.Flush()
	ioutil.WriteFile(file, buffer.Bytes(), os.ModePerm)
}
func gden(file string) {
	b, _ := ioutil.ReadFile(file)
	buffer := new(bytes.Buffer)
	buffer.Write(b)
	r, _ := gzip.NewReader(buffer)
    defer r.close()
	data, _ := ioutil.ReadAll(r)
	fmt.Println(file, " result Data:", string(data))
}

This result:
Go to Go okay.
PHP to Go okay.

答案1

得分: 5

以下是翻译好的内容:

使用以下更改可以使其正常工作:

  • 在你的 PHP 代码中,你想要使用 gzdecode 而不是 gzinflate。如果使用 gzdecode,你就不需要使用 substr($data, 10) 这样的代码了。我没有详细了解 deflate 和 gzip 的关系,但简单来说,gzencode/gzdecode 与 golang 的 gzip 包以及 GNU 命令行工具中的 gz* 家族所做的工作是相匹配的。
  • 在你的 Go 代码中,将 gzip.Writer.Close() 的调用放在从 buffer 读取之前。你可以在这里看到:http://golang.org/src/compress/gzip/gzip.go?s=6230:6260#L240,在关闭时会向底层流写入一些额外的内容,所以在你上面的示例中,你写入的内容是不完整的。(defer 语句会导致 Close() 在包含它的函数退出后运行。)很可能 Go 的 gzip 解码可以成功解码,而 PHP 的实现则不行 - 无论如何,你应该正确关闭对内存缓冲区的流,以确保在将其写入文件之前它是完整的。

必要的注意事项:你在 Go 代码中忽略了所有的错误。这段代码看起来只是一个测试,所以我不会过多强调,但你肯定应该进行适当的错误处理(要么向用户报告问题,要么向调用你的函数的代码报告问题)。

英文:

It works with the following changes:

  • In your PHP code you want to use gzdecode instead of gzinflate. And you don't need this substr($data, 10) stuff if you use that. I didn't read up on how deflate relates to gzip but the simplicity is that gzencode/gzdecode match what the golang gzip package does and what the gz* family of GNU command line tools do.
  • In your Go code move your gzip.Writer.Close() call to be done before you read from buffer. As you can see here: http://golang.org/src/compress/gzip/gzip.go?s=6230:6260#L240 there is some additional stuff that is written to the underlying stream upon close, so in your example above what you are writing is incomplete. (The defer statement causes Close() to be run after the containing function exits.) Most likely the Go gzip decoding is managing to decode anyway whereas the PHP implementation is not - in any case you should properly close the stream to your in memory buffer to ensure it is complete before writing it out to a file.

OBLIGATORY NOTE: You are ignoring all of your errors in your Go code. This code looks like just a test so I won't belabor the point but you definitely want to be doing proper error handling (either reporting the problem to the user or to the caller of your function).

huangapple
  • 本文由 发表于 2015年8月1日 15:20:05
  • 转载请务必保留本文链接:https://go.coder-hub.com/31759354.html
匿名

发表评论

匿名网友

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

确定