【Copy攻城獅日誌】Node.js之http下載圖片失敗

Created 2019-4-5 22:24:33 by huqi
Updated 2019-4-5 23:23:56 by huqi

clipboard.png
↑開局一張圖,故事全靠編↑

從解答一個知否上的問題說起

有時候,自己就像自己在知否的簽名一樣--我以爲的就是我以爲的?一直以來,對前端技術的一知半解一葉障目,導致我遇到問題總是片面,比如這次,就翻車了。技術水平就那樣,然後我居然還想着幫人家解答,這不是誤人子弟嗎?昨天解答的這個問題,是關於node.js的http方法,根據Url獲取網絡圖片寫入到本地文件夾,這個需求我之前是玩過的,但用的是Copy&Paste的代碼,也沒有細細研究源碼,只知道用的是request的模塊,不過這次的哥們沒有依賴任何第三方模塊,只是用的內置的http模塊。他遇到的問題就是上圖所見,有一張圖片沒有下載成功無法正常顯示。具體問題見@夜鷹 :用Node.js讀取遠程的圖片文件並寫入本地?

clipboard.png

通過內置http模塊下載圖片源碼

  • 引入內置http模塊發起請求獲取文件
  • 引入內置fs模塊寫入文件
const http = require('http')
const fs = require('fs')

const urlArr = [
'http://img.zcool.cn/community/01e505554437be0000019ae95582a2.jpg@900w_1l_2o_100sh.jpg',
'http://static.pig66.com/uploadfile/2017/1102/20171102095531217.png',
]

urlArr.forEach(url => {
    getImg(url)
})

function getImg(url, name) {
    http.get(url, {encoding: null}, res => {
        let img = []
        let size = 0
        // 將圖片地址以【.】符號分割,取最後一項,即爲格式後綴
        const _arr = url.split('.')
        const format = _arr[_arr.length - 1]
        // 如果沒有傳入圖片名字,則使用隨機數
        const _name = name ? name : 'image-' + Math.floor(Number(new Date()) * Number(Math.random()))
        res.on('data', chunk => {
            img.push(chunk)
            size += chunk.length
        })
        res.on('end', () => {
            // 合併 Buffer
            const buffer = Buffer.concat(img, size)
            fs.writeFileSync(`img/${_name}.${format}`, buffer, (err) => {
                if (err) {
                    return console.error(err);
                }
                console.log("數據寫入成功!");
            })
        })
    })
}

對來說,起初我以爲是文件太大的原因,因爲通過輸出查看到Buffer數據中斷並直接結束了。後來我試了下1M左右的圖片,完全能夠成功下載,然而,打臉啪啪啪。接下來,我草率地下了結論,並丟給博主一段使用第三方模塊request的同樣功能的實現(見歷史版本:共被編輯 4 次)。真相糾結是怎樣的?另一位答主@啊哦 已經給出了相當明確的答案!

clipboard.png

“罪魁禍首”--301重定向

301重定向()頁面永久性移走)是一種非常重要的“自動轉向”技術。網址重定向最爲可行的一種辦法。當用戶或搜索引擎向網站服務器發出瀏覽請求時,服務器返回的HTTP數據流中頭信息(header)中的狀態碼的一種,表示本網頁永久性轉移到另一個地址。

打開圖片鏈接:http://www.pig66.com/uploadfi...,通過查看Network,我們清晰地看到源圖片有做301重定向。通過在源代碼中添加日誌輸出,我們也能清楚地看到301的狀態碼。
clipboard.png

clipboard.png

既然問題的根源已經找到,那就對症寫bug,如果是301的話獲取請求返回的真實地址再次發起請求。

        const { statusCode } = res
        if ( statusCode === 301 ) {
            const url = res.headers['location']
            return getImg(url)
        }

修改後的代碼:

const http = require('http')
const fs = require('fs')

const urlArr = [
'http://img.zcool.cn/community/01e505554437be0000019ae95582a2.jpg@900w_1l_2o_100sh.jpg',
'http://static.pig66.com/uploadfile/2017/1102/20171102095531217.png',
]

urlArr.forEach(url => {
    getImg(url)
})

function getImg(url, name) {
    http.get(url, {encoding: null}, res => {
        const { statusCode } = res
        console.log(statusCode)
        if ( statusCode === 301 ) {
            const url = res.headers['location']
            return getImg(url)
        }
        let img = []
        let size = 0
        // 將圖片地址以【.】符號分割,取最後一項,即爲格式後綴
        const _arr = url.split('.')
        const format = _arr[_arr.length - 1]
        // 如果沒有傳入圖片名字,則使用隨機數
        const _name = name ? name : 'image-' + Math.floor(Number(new Date()) * Number(Math.random()))
        res.on('data', chunk => {
            img.push(chunk)
            size += chunk.length
        })
        res.on('end', () => {
            // 合併 Buffer
            const buffer = Buffer.concat(img, size)
            fs.writeFileSync(`img/${_name}.${format}`, buffer, (err) => {
                if (err) {
                    return console.error(err);
                }
                console.log("數據寫入成功!");
            })
        })
    })
}

成功拿到圖片,並能直觀的感受到301重定向之後又發起了一次請求,

clipboard.png

clipboard.png

後記

這兩天朋友託我寫兩個簡單的頁面,我發現自己啥也不會!想想我,居然還這麼熱心地去幫人解答,真的是誤人子弟害人不淺。謹以此次經歷深刻反省自我,對被我坑過的各位表示深切的歉意。同時,也希望各位大佬不惜多多賜教!最後,祝@jsliang 生日快樂!寫在生日,一年前端拼搏記

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章