2017-07-03 283 views
-1

我在解压缩之前压缩过的字节流时遇到了问题。基本上,我试图使用函数bytes.NewReader()创建一个读取器,然后使用gzip.NewReader()函数解压缩流。最后,我想以字符串或字节格式返回实际值。Golang:gzip.NewReader()在给出类型Reader时返回nil

我知道gzip.NewReader要求io.Reader作为输入,但据我所知,类型Reader实现了接口io.Reader。我认为这不应该导致任何错误,但我想知道在这种情况下可能会出现什么问题。如果你能帮我解决这个问题,我会非常感激!

如果你想知道这一段文字是,

“amZzRUR2NHVtcVpiZHNROHJiTTNYeGdUSndGTlVDZC9jaElSK1lXcFlJOD0 =”

这是从我的客户Python脚本发送采样输入。它使用gzip压缩,使用AES128进行加密,最后按照base64进行编码。

客户端代码:

import time 
import json 
import requests 
import random 
import gzip 
import base64 

from Crypto.Cipher import AES 

baseurl = 'http://0.0.0.0:80' 
key = 'TfvY7I358yospfWKcoviZizOShpm5hyH' 
iv = 'mb13KcoviZizvYhp' 

MODE = AES.MODE_CFB 
BLOCK_SIZE = 16 
SEGMENT_SIZE = 128 

def http_post(url, data): 
    print('Going to make a request to {} with the following data: {}'.format(url, data)) 
    r = requests.post(url, 
        data=data, 
        headers={'Content-type': 'application/json; charset=utf-8', 
          'Connection': 'keep-alive'},) 
    if r.status_code != 200: 
     print('Server returned unexpected response code {}, and content: {}'.format(r.status_code, r.content)) 
     return False 
    else: 
     data = r.json() 
     return data 


def post_sample_data(key, iv): 
    fake_device_id = "MB88" 
    Load_VA_Total_Mean = random.randint(1000, 100000) 
    print('Data that should come back: {}'.format(Load_VA_Total_Mean)) 
    data = {'i': fake_device_id, 'p': [ 
    {'d': [54.3, 0, 99, 49.35, 3, 99, 51.533, 1, 98, 28964, 7348, 43590, Load_VA_Total_Mean, 10350, 55200, 49.7], 
    't': time.time(), 'dt': 'p'}]} 
    url = baseurl + '/realtimedata' 
    encryption_key_reference = 1 
    payload = '{}\n{}'.format(convert_pack(data, key, iv), encryption_key_reference) 
    return http_post(url, payload) 


def convert_pack(inputdict, key, iv): 
    jsonpayload = json.dumps(inputdict) # encode dict to json string 
    gzippayload = gzip.compress(jsonpayload.encode('utf-8')) # compress with gzip 
    encryptedpayload = base64.b64encode(encrypt(key, iv, message)) 
    encoded = base64.b64encode(encryptedpayload) 
    print('encoded: {}'.format(encoded)) 

    return str(encoded, encoding='utf-8') 


def _pad_string(value): 
    length = len(value) 
    pad_size = BLOCK_SIZE - (length % BLOCK_SIZE) 
    return value.ljust(length + pad_size, '\x00') 


def encrypt(key, iv, plaintext): 
    aes = AES.new(key, MODE, iv, segment_size=SEGMENT_SIZE) 
    plaintext = _pad_string(plaintext) 
    encrypted_text = aes.encrypt(plaintext) 
    return encrypted_text 


post_sample_data(key, iv) 

服务器代码:

package main 

import (
"crypto/aes" 
"crypto/cipher" 
"crypto/rand" 
"encoding/base64" 
"fmt" 
"io" 
"compress/gzip" 
"io/ioutil" 
"bytes" 
) 

func main() { 
    receivedText := "amZzRUR2NHVtcVpiZHNROHJiTTNYeGdUSndGTlVDZC9jaElSK1lXcFlJOD0=" 
    fmt.Println("encrypted + encoded + gzipped: ", originalText) 
    key := []byte("TfvY7I358yospfWKcoviZizOShpm5hyH") 

    text := decrypt(key, originalText) 
    fmt.Println("decrypted: ", string(text)) 
    reader := bytes.NewReader(text) 
    gzReader, err1 := gzip.NewReader(reader) 
    fmt.Println(gzReader) 

    if err1 != nil { 
     fmt.Println("error1") 
    } 

    content, err2 := ioutil.ReadAll(gzReader) 

    if err2 != nil { 
     fmt.Println("error2") 
    } 

    fmt.Println(string(content)) 
} 

func decrypt(key []byte, cryptoText string) []byte { 
    ciphertext, _ := base64.StdEncoding.DecodeString(cryptoText) 
    fmt.Println("decoded: ", string(ciphertext)) 
    block, err := aes.NewCipher(key) 
    if err != nil { 
     panic(err) 
    } 

    iv := []byte("mb13KcoviZizvYhp") 
    stream := cipher.NewCFBDecrypter(block, iv) 
    stream.XORKeyStream(ciphertext, ciphertext) 

    return ciphertext 
} 

输出:

<nil> 
error1 
panic: runtime error: invalid memory address or nil pointer dereference [recovered] 
    panic: runtime error: invalid memory address or nil pointer dereference 
[signal SIGSEGV: segmentation violation code=0x1 addr=0x288 pc=0x10a9167] 

goroutine 1 [running]: 
io/ioutil.readAll.func1(0xc420037dd0) 
    /usr/local/go/src/io/ioutil/ioutil.go:30 +0x119 
panic(0x10c4580, 0x1154d00) 
    /usr/local/go/src/runtime/panic.go:489 +0x2cf 
compress/gzip.(*Reader).Read(0x0, 0xc420092000, 0x200, 0x200, 0x1024ade, 0xc400000008, 0xc4200120c0) 
    /usr/local/go/src/compress/gzip/gunzip.go:247 +0x37 
bytes.(*Buffer).ReadFrom(0xc420037d28, 0x11451a0, 0x0, 0xc420092000, 0x0, 0x200) 
    /usr/local/go/src/bytes/buffer.go:179 +0x160 
io/ioutil.readAll(0x11451a0, 0x0, 0x200, 0x0, 0x0, 0x0, 0x0, 0x0) 
    /usr/local/go/src/io/ioutil/ioutil.go:33 +0x150 
io/ioutil.ReadAll(0x11451a0, 0x0, 0x1, 0x7, 0x0, 0x0, 0x20) 
    /usr/local/go/src/io/ioutil/ioutil.go:42 +0x3e 
main.main() 
    /Users/bkaankuguoglu/Desktop/Go-dev/tool-backend/tester.go:43 
+0x404 
exit status 2 
+1

你表明它返回一个错误(应该首先检查),那到底是什么问题? – JimB

+1

打印实际的错误信息,它会告诉你它为什么失败。 “这里的一些文本”不是gzip的虚拟输入。 – Art

+0

@JimB我现在编辑了我的问题,谢谢您的反馈! – bkaankuguoglu

回答

1

的问题是, “内容”,由您通过阅读器生产到gz.NewReader()不是有效的gzip流(数据)。如果gzip.NewReader()返回非nil错误(与您的情况一样),返回的gzReader可能是nil(通常是)。

如果您将有效的gzip流传递到gzip.NewReader(),返回的gzReader将不会是nil,并且解码将成功。

例如文本"hello"的gzip的编码形式是:

[31 139 8 0 0 0 0 0 0 255 203 72 205 201 201 7 0 134 166 16 54 5 0 0 0] 

使用这个作为输入:

data := []byte{31, 139, 8, 0, 0, 0, 0, 0, 0, 
    255, 203, 72, 205, 201, 201, 7, 0, 134, 166, 16, 54, 5, 0, 0, 0} 
reader := bytes.NewReader(data) 
// The rest is unchanged 

它的工作原理,并且该输出(尝试在Go Playground) :

&{[31 139 8 0 0 0 0 0 0 255 203 72 205 201 201 7 0 134 166 16 54 5 0 0 0] 0 -1} 
&{{ [] 0001-01-01 00:00:00 +0000 UTC 255} 0x10440260 0x10462000 0 0 [31 139 8 0 0 0 0 0 0 255 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0] <nil> true} 
hello 
+0

感谢您的答案!在我的程序中,首先,gzip输入字符串,然后使用aes256进行加密,最后在base64中对文本进行编码。我检查了加密和编码日期的最后两个步骤,看起来好像没有gzip步骤就可以正常工作。什么是有效的gzip流?像这样? \ x1f \ x8b \ x08 \ x00 \ xcd1ZY \ x02 \ xff%\ x8e \ xb1 ... – bkaankuguoglu

+0

@bkaankuguoglu我不知道gzip规范的内心,我无法从字节中知道。如果它是有效的,'gzip'包应该接受它。如果它不接受它,这很可能是无效的。在你的例子中,你应该改变步骤:首先base64解码,然后解码aes256,然后你可以将结果传递给gzip解码。 – icza

+0

我添加了我的脚本的完整版本。我按照您所描述的顺序执行这些步骤。 – bkaankuguoglu