【Copy攻城狮日志】Node.js之http下载图片失败

7次阅读

共计 2767 个字符,预计需要花费 7 分钟才能阅读完成。

Created 2019-4-5 22:24:33 by huqi Updated 2019-4-5 23:23:56 by huqi
↑开局一张图,故事全靠编↑
从解答一个知否上的问题说起
有时候,自己就像自己在知否的签名一样 – 我以为的就是我以为的?一直以来,对前端技术的一知半解一叶障目,导致我遇到问题总是片面,比如这次,就翻车了。技术水平就那样,然后我居然还想着帮人家解答,这不是误人子弟吗?昨天解答的这个问题,是关于 node.js 的 http 方法,根据 Url 获取网络图片写入到本地文件夹,这个需求我之前是玩过的,但用的是 Copy&Paste 的代码,也没有细细研究源码,只知道用的是 request 的模块,不过这次的哥们没有依赖任何第三方模块,只是用的内置的 http 模块。他遇到的问题就是上图所见,有一张图片没有下载成功无法正常显示。具体问题见 @夜鹰:用 Node.js 读取远程的图片文件并写入本地?

通过内置 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 次)。真相纠结是怎样的?另一位答主 @啊哦 已经给出了相当明确的答案!

“罪魁祸首”–301 重定向
301 重定向()页面永久性移走)是一种非常重要的“自动转向”技术。网址重定向最为可行的一种办法。当用户或搜索引擎向网站服务器发出浏览请求时,服务器返回的 HTTP 数据流中头信息 (header) 中的状态码的一种,表示本网页永久性转移到另一个地址。
打开图片链接:http://www.pig66.com/uploadfi…,通过查看 Network,我们清晰地看到源图片有做 301 重定向。通过在源代码中添加日志输出,我们也能清楚地看到 301 的状态码。

既然问题的根源已经找到,那就对症写 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 重定向之后又发起了一次请求,

后记
这两天朋友托我写两个简单的页面,我发现自己啥也不会!想想我,居然还这么热心地去帮人解答,真的是误人子弟害人不浅。谨以此次经历深刻反省自我,对被我坑过的各位表示深切的歉意。同时,也希望各位大佬不惜多多赐教! 最后,祝 @jsliang 生日快乐!写在生日,一年前端拼搏记

正文完
 0