時間: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()
。我們將除了二進位制流以外的內容放在form
的header
中,二進位制流append
到form
中。
首先我們需要定義請求配置檔案:
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
,如果有同學有更好的想法可以評論留言,歡迎討論。