简体   繁体   English

在单个本地 json 文件中保存对多个 GET 请求的响应 - node.js

[英]Save responses to multiple GET requests in a single local json file - node.js

The problem:问题:

I have a function that maps over countries and regions and creates an array of urls, then makes a GET request to each one.我有一个 function 映射国家和地区并创建一组 url,然后向每个 url 发出 GET 请求。 I want to save the responses in a single json file, and I want this function to handle that as well.我想将响应保存在一个 json 文件中,我希望这个 function 也能处理它。

Expected results:预期成绩:

I expected to be able to run the function as needed (like when source data is updated), and get a new or updated local json file with all the data objects in one array.我希望能够根据需要运行 function(例如更新源数据时),并获得一个新的或更新的本地 json 文件,其中包含一个数组中的所有数据对象。

Actual results:实际结果:

A file with only one record, an array with the last response object.一个只有一条记录的文件,最后一个响应为 object 的数组。

What I've tried:我试过的:

I tried using fs.writeFile and fs.readFile .我尝试使用fs.writeFilefs.readFile I did not get any errors, but the resulting file had only one record, even though console showed all the requests being made.我没有收到任何错误,但生成的文件只有一条记录,即使控制台显示了所有正在发出的请求。 It seemed that each response was being written over the previous.似乎每一个回应都被写在了前一个之上。

Minimum reproducable (node.js) example:最小可重现(node.js)示例:

const fs = require('fs')

// subset of countries and codes for demo purposes
const countryDirList = [
  'antarctica',
 'central-asia',
 ]

const fbCountryCodes = [
 { "region": "antarctica", "codes": ["ay", "bv"] },
 { "region": "central-asia", "codes": ["kg", "kz"] },
]

const callingUrlsInSequence = async () => {
  fs.writeFile('./test.json', '[]', function (err) { 
    if (err) throw err
    console.log('File - test.json - was created successfully.')
  })
  try {
    const urlConstructor = countryDirList.map(async (country) => {
      console.log('countries mapped', country)
      return fbCountryCodes.filter(async (f) => {
        if (country === f.region) {
          const urls = f.codes.map(async (c) => {
            const response = await axios({
              method: 'get',
              url: `https://raw.githubusercontent.com/factbook/factbook.json/master/${country}/${c}.json`,
              responseType: 'json',
              headers: {
                'Content-Type': 'application/json',
              },
            })
           
            fs.readFile('./test.json', function (err, data) {
              let json = JSON.parse(data)
              json.push(response.data)
              setTimeout(() => {
                fs.writeFile('./test.json', JSON.stringify(json), function (err) { 
                  if (err) throw err
                  console.log('The "data to append" was appended to file!')
                })
              }, 1000)
            })
            return response.data
          })
          const dataArr = await Promise.all(urls)
          dataArr.map((item) =>
            console.log(
              'dataArr',
              item.Government['Country name']['conventional short form']
            )
          )
        }
      })
    })
  } catch (err) {
    console.log('axios error: ', err)
  }
}
callingUrlsInSequence()

I'm re-writing this question now because it kept getting downvoted, and I could see that it was not very concise.我现在正在重写这个问题,因为它一直被否决,而且我可以看出它不是很简洁。

I can also see now, that obviously, the fs.readFile inside the fs.writeFile is not going to work in the code I provided, but I'm leaving it there in case it might help someone else, combined with the solution I provided in response to my own question.我现在也可以看到,很明显, fs.readFile中的fs.writeFile不会在我提供的代码中工作,但我将它留在那里以防它可能对其他人有帮助,并结合我提供的解决方案回答我自己的问题。

I ended up learning how to solve this problem with both node-fetch and axios. They are not exactly the same.我最终学习了如何使用 node-fetch 和 axios 解决这个问题。它们并不完全相同。

For both: First, check for existence of destination file, and create one if it's not already there.对于两者:首先,检查目标文件是否存在,如果不存在则创建一个。


    const createNew = () => {
      try {
        if (existsSync('./data.json')) {
          console.log('file exists')
          return
        } else {
          writeFile('./data.json', '[]', (error, data) => {
            if (error) {
              console.log('fs.writeFile - create new - error: ', error)
              return
            }
          })
        }
      } catch (err) {
        console.log('fs.existsSync error: ', err)
      }
    }
    
    createNew()

Then make the array of urls:然后制作网址数组:


    const countryDirList = [...countries]
    
    const fbCountryCodes = [...codes]
    
    const urls = []
    
    // maybe a reducer function would be better, but my map + filter game is much stronger X-D
    const makeUrls = (countriesArr, codesArr) =>
      countriesArr.map((country) => {
        return codesArr.filter((f) => {
          if (country === f.region) {
            return f.codes.map((code) => {
              return urls.push(
                `https://raw.githubusercontent.com/factbook/factbook.json/master/${country}/${code}.json`
              )
            })
          }
        })
      })
    
    makeUrls(countryDirList, fbCountryCodes)

Next, make the requests.接下来,提出要求。

Axios: Axios:


  fs.readFile('./data.json', (error, data) => {
  if (error) {
    console.log(error)
    return
  }

  Promise.all(
    urls.map(async (url) => {
      let response
      try {
        response = await axios.get(url)
      } catch (err) {
        console.log('axios error: ', err)
        return err
      }
      return response
    })
  )
    .then((res) => {
      const responses = res.map((r) => r.data)

      fs.writeFile('./data.json', JSON.stringify(responses, null, 2), (err) => {
        if (err) {
          console.log('Failed to write data')
          return
        }
        console.log('Updated data file successfully')
      })
    })
    .catch((err) => {
      console.log('axios error: ', err)
    })
})

Node-fetch:节点获取:


  //same basic structure, readFile with fetch and write file inside
    fs.readFile('./data2.json', (error, data) => {
      if (error) {
        console.log(error)
        return
      }
    
      async function fetchAll() {
        const results = await Promise.all(
          urls.map((url) => fetch(url).then((r) => r.json()))
        )
    
        fs.writeFile('./data2.json', JSON.stringify(results, null, 2), (err) => {
          if (err) {
            console.log('Failed to write data')
            return
          }
          console.log('Updated data file successfully')
        })
      }
    
      fetchAll()
    })

Both methods produce exactly the same output: a json file containing a single array with however many response objects in it.这两种方法都产生完全相同的 output:一个 json 文件,其中包含一个数组,其中包含许多响应对象。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM