將圖片轉化為二進位制流附在表單中傳送出去

rexkentzheng發表於2018-11-15

時間:2018年11月14日
這是在和媒體對接時遇到的一個問題,不得不說有的媒體是真的變態。
一般情況下媒體對接是比較簡單的,就是把我們本地的資料傳送給媒體,之後媒體給我們反饋,生成一個m_id,我們拿到這個m_id之後會存在資料庫裡,這就完成了媒體對接的一半。之後我們通過m_id來查詢傳送的資料是否通過稽核,這也就完成了媒體對接了。
理想的情況如上所示,可是I媒體它跟別人不一樣。我們本地的資料一般情況下都有圖片或者視訊,在對接I媒體時只有圖片。之前遇到這種情況都是將圖片的連結傳送給媒體,因為這些圖片都是儲存在我們的檔案伺服器上的。但是I媒體不想要圖片連結,希望我們把圖片下載好了然後傳送過去。
圖片總共有5張,還要分兩組傳送,一組兩張一組三張,之後會給我們返回兩個m_id。 傳送的資料結構如下所示:

const link_1 = [ 'link_1_1', 'link_1_2' ];
const link_2 = [ 'link_2_1', 'link_2_2', 'link_2_3' ]
const all_img_urls: [link_1, link_2] 
const header = {
  a: 'a',
  b: 'b',
}
複製程式碼

如上所示,all_img_urls中有兩個元素,這兩個元素是兩個陣列,裡面包含了具體的每條url
這裡的處理就比較麻煩了,得先將配置檔案解析成兩個,再將圖片轉化為二進位制流,之後附在form中傳送過去。
關於解析二進位制流,本意是想用https請求來獲取的,後來發現request也可以將檔案轉化成二進位制流,文件在這裡。
關於request的操作其實是比較複雜的,這裡我們選擇了進階用法——r.form()。我們將除了二進位制流以外的內容放在formheader中,二進位制流appendform中。
首先我們需要定義請求配置檔案:

const requestOptions = {
  method: httpMethod
  uri: api
  headers: headers
  localPath: url
}
複製程式碼

之後使用Promise.all生成兩個配置檔案:

const generateRequsetOption = () => {
  return Promise.all(_.map(all_img_urls, (imgUrls, index) => {
    return {
      method: 'POST',
      uri: api,
      headers: headers,
      imgUrls: imgUrls
    };
  }));
}
複製程式碼

之所以使用Promise.all是因為下面呼叫的時候還是需要使用Promise,所以配置檔案可以直接是兩個Promise物件。
接下來我們需要先將圖片地址轉化為二進位制流,在獲取到二進位制流之後再進行下一步操作:

generateIqiyiRequsetOption()
.then((options) => {
  Promise.all(_.map(options, (option) => {
    Promise.all(_.map(option.img_urls, (img_url) => {
      new Promise((_resolve, _reject) => {
        request({ uri: imgUrl, encoding: null }, (err, response, body) => {
          if(err){
            return _reject(err)
          }
          return _resolve(body)
        })
      })
    }))
    .then((bodys) => {
      // 下一步操作
    })
  }))
})
複製程式碼

這裡的Promise較多,主要是因為需要處理的東西比較多,首先我們要將兩個配置檔案迴圈出來,使用一次Promise.all,之後我們將配置檔案中的img_urls迴圈出來,使用了一次Promise.all,最後我們使用Promise包裹我們請求二進位制流的過程,使用了一次Promise。確實比較複雜,但是也沒有辦法,就這樣,我們在.then()中獲取的body就是轉換之後的二進位制流。下面我們使用request來傳送資料。

.then((bodys) => {
  return new Promise((_resolve, _reject) => {
    //  定義request的主體內容
    const r = request(_.omit(option, 'img_urls'), (err, response, body) => {
      if(err){
        return _reject(err);
      }
      return _resolve(body);
    })
    const form = r.form();
    _.each(option.img_urls, (img_url, index) => {
      form.append("file_${index}", bodys[index], { filename: img_url.split('/').pop() });
    });
  })
})
複製程式碼

在獲取到二進位制流之後我們使用了Promise來處理配置資訊,先定義request的主體內容,之後根據配置檔案新建form,之後我們迴圈img_urls,使用index來獲取到bodys中的二進位制流,還有就是新增一些檔名之類的資訊。
至此,我們就完成了資料整合傳送到主體內容,全部程式碼如下所示:

const link_1 = [ 'link_1_1', 'link_1_2' ];
const link_2 = [ 'link_2_1', 'link_2_2', 'link_2_3' ];
const all_img_urls: [link_1, link_2] 
const header = {
  a: 'a',
  b: 'b',
};
const requestOptions = {
  method: httpMethod
  uri: api
  headers: headers
  localPath: url
};
const generateRequsetOption = () => {
  return Promise.all(_.map(all_img_urls, (imgUrls, index) => {
    return {
      method: 'POST',
      uri: api,
      headers: headers,
      imgUrls: imgUrls
    };
  }));
};
return new Promise ((resolve, reject) => {
  generateIqiyiRequsetOption()
  .then((options) => {
    Promise.all(_.map(options, (option) => {
      Promise.all(_.map(option.img_urls, (img_url) => {
        return new Promise((_resolve, _reject) => {
          request({ uri: imgUrl, encoding: null }, (err, response, body) => {
            if(err){
              return _reject(err);
            }
            return _resolve(body);
          });
        });
      }))
      .then((bodys) => {
        return new Promise((_resolve, _reject) => {
          const r = request(_.omit(option, 'img_urls'), (err, response, body) => {
            if(err){
              return _reject(err);
            }
            return _resolve(body);
          });
          const form = r.form();
          _.each(option.img_urls, (img_url, index) => {
            form.append("file_${index}", bodys[index], { filename: img_url.split('/').pop() })
          });
        })
      })
    }))
    .then((results) => {
      //  處理返回資料
    })
  })
});
複製程式碼

程式碼其實比較複雜,需要多看看才能理解,主要就是因為請求是非同步的,可前端要同時得到反饋,所以只能一層又一層的巢狀Promise,如果有同學有更好的想法可以評論留言,歡迎討論。

相關文章