首页 > 解决方案 > io.Reader 可以接受文件描述符吗?“JSON 输入意外结束”

问题描述

json.Unmarshal在将打开的文件描述符从 os.Create() 传递给接受类型 io.Reader 并运行 io.Copy(b.Bytes(), reader) 的函数后,有效 JSON 的失败

以下代码块中的Read()方法是否正确实现?io.Reader包装该Read方法,但会将打开的文件描述符传递给它将文件读取为字节,从而允许 io.Copy(b.Bytes(), reader) 将数据复制到 var b?

有没有更好的方法可以在不使用的情况下做到这一点ioutil.ReadAll

  1. 已验证 JSON 完全有效。使用 jq 和 3 个在线 JSON 验证器进行验证。
  2. 通过另一个实现 io.Write 的函数验证数据实际上正在成功写入文件。
  3. 已验证ioutil.ReadAll可以将 JSON 读取为字节,因此我认为正在使用的方法实现不正确。

** 这不是我的代码,我正在对其他人编写的代码进行故障排除,无法回答任何问题 **

我有一个 json 文件collected_data.json

该文件是用 os.Create 创建的

file, err := os.Create("/var/log/collected_data.json")

配置了一个结构,它设置fileDataStore

profiler := &collectors.SystemProfiler{
  DataStore:         file,
  NetworkInterfaces: interfaces,
  ApiURL:            *apiURL,
  RunOnce:           *runOnce,
  SysTag:            *sysTag,
}

然后我们运行一个Gather()方法。

err = profiler.Send(output)
if err != nil {
  log.Error(err)
}

Send()方法实现了 SystemProfiler 结构:

func (s *SystemProfiler) Send(profile *SystemProfile) error {...}

到目前为止一切正常,直到我们尝试从/var/log/collected_data.json.

在这个Send()方法中,我们尝试/var/log/collected_data.json在两种情况下读取文件。

1,如果文件不为空,我们读取文件,并用它做一些事情(这里没有显示)。

data, err := store.Read(s.DataStore)
if err != nil {
  log.Print("I couldn't read the datastore")
  return err
}

2,如果文件不为空,我们将数据写入文件,然后立即将其读回并解组,以满足稍后reflect.DeepEqual在数据比较和写入文件的数据之间进行的函数。

在这两种情况下,该Read()方法都返回“JSON 输入意外结束”,文件中包含有效的 JSON /var/log/collected_data.json。用于写入数据的方法工作得很好。

{"level":"info","msg":"I couldn't read the datastore","time":"2019-08-02T02:26:42-04:00"}
{"level":"error","msg":"unexpected end of JSON input","time":"2019-08-02T02:26:42-04:00"}

Read()方法如下所示:

// Read reads JSON data from an io.Reader
func Read(reader io.Reader) (interface{}, error) {
  var data interface{}
  var b bytes.Buffer
  io.Copy(&b, reader)

  err := json.Unmarshal(b.Bytes(), &data)
    if err != nil {
      return nil, err
    }
  return data, nil
}

预期成绩:

有效的 JSON 从 io.Reader 类型的读取器复制到 bytes.Buffer 类型的 b,成功解组并返回。

实际结果:{"level":"error","msg":"JSON 输入意外结束","time":"2019-08-02T02:26:42-04:00"}

要回答评论中提出的问题,这里是Send()函数内的代码块:

var storedProfile SystemProfile

    check := store.IsEmpty(s.DataStore)

    if check == false {
        log.Print("Data Store Exists") 
        // If there is data stored, read it
        data, err := store.Read(s.DataStore)
        if err != nil {
            return err
        }
        //var tmp SystemProfile
        err = mapstructure.Decode(data, &storedProfile)
        if err != nil {
            return err
        }
    } else {
        log.Print("Data Store Doesn't Exist")
        // If the data store is empty, write to it
        err := store.Write(s.DataStore, profile)
        if err != nil {
            return err
        }
        data, err := store.Read(s.DataStore)
        if err != nil {
            log.Print("I couldn't read the datastore")
            return err
        }
        err = mapstructure.Decode(data, &storedProfile)
        if err != nil {
            log.Print("I couldn't decode the json to a map")
            return err
        }

        body, err := json.Marshal(profile)
        if err != nil {
            return err
        }
        _, err = client.Post(s.ApiURL, "application/json", bytes.NewBuffer(body)) // TODO: Handle response from API here
        log.Print(client.LogString())
    }

if !reflect.DeepEqual(storedProfile, profile ) {
        // If what's in the data store and what has been collected are different,
        // write the recently collected data to the data store and send it out to the API
        store.Write(s.DataStore, profile)
        body, err := json.Marshal(profile)
        if err != nil {
            return err
        }
        _, err = client.Post(s.ApiURL, "application/json", bytes.NewBuffer(body)) // TODO: Handle response from API here
        if err != nil {
            return err
        }
        log.Print(client.LogString())
    }

标签: go

解决方案


这个问题的答案是肯定的。可以*os.File用作io.Reader.

问题是应用程序将数据写入文件,然后尝试从文件中读取相同的数据,而不回溯数据的写入位置。

通过在调用 store.Write 和调用 store.Read 之前添加以下代码来解决此问题。

 if _, err := s.DataStore.Seek(io.SeekStart, 0); err != nil {
     return err
 }

推荐阅读