将图像写入本地服务器

66

更新

去年接受的答案还不错,但现在我会使用其他人都在用的软件包: https://github.com/mikeal/request


原始内容

我正在尝试使用node.js抓取谷歌的标志并将其保存到我的服务器上。

这是我现在拥有的代码,但它不起作用:

        var options = {
            host: 'google.com',
            port: 80,
            path: '/images/logos/ps_logo2.png'
        };

        var request = http.get(options);

        request.on('response', function (res) {
            res.on('data', function (chunk) {
                fs.writeFile(dir+'image.png', chunk, function (err) {
                    if (err) throw err;
                    console.log('It\'s saved!');
                });
            });
        });

我该怎样让它正常工作?

6个回答

93

以下是几个要点:

  1. 我假设你已经使用了fs/http模块,并设置了dir变量 :)
  2. google.com会重定向到www.google.com,所以你保存的是重定向响应的正文内容,而不是图片
  3. 响应是流式传输的。这意味着"data"事件会多次触发,而不是一次。你必须将所有块保存并拼接在一起才能获得完整的响应正文内容
  4. 由于你正在获取二进制数据,因此必须相应地设置response和writeFile的编码(默认为utf8)

以下代码应该可以正常工作:

var http = require('http')
  , fs = require('fs')
  , options

options = {
    host: 'www.google.com'
  , port: 80
  , path: '/images/logos/ps_logo2.png'
}

var request = http.get(options, function(res){
    var imagedata = ''
    res.setEncoding('binary')

    res.on('data', function(chunk){
        imagedata += chunk
    })

    res.on('end', function(){
        fs.writeFile('logo.png', imagedata, 'binary', function(err){
            if (err) throw err
            console.log('File saved.')
        })
    })

})

17
为什么你不使用分号? - Zhianc
3
@jcdavid http://blog.izs.me/post/2353458699/an-open-letter-to-javascript-leaders-regarding - Ricardo Tomasi
7
这是糟糕的建议。永远不要使用.setEncoding('binary'),而应该使用res.pipe(fs.createWriteStream())来代替在内存中缓冲整个图像。如果你懒得自己做,可以使用这个库:https://github.com/stream-utils/save-to - Jonathan Ong
1
@JonathanOng 2011年,如果我没记错的话,当时需要设置编码。关于流方面,同意你的想法,随时欢迎提出修改建议。 - Ricardo Tomasi
@JimmyBreck-McKye 这是真的。ES的作者们也说过,依赖ASI并不明智。我现在有点犹豫,是要求对ASI的工作原理有很好的了解,从而避免一些情况,还是只是到处使用分号。老实说,我喜欢使用更少的字符——看起来更清爽。然而,人们应该知道在哪些情况下需要使用分号,因为我看到开发人员到处添加分号……在块定义(函数、if、else等)之后。 - Martin Muzatko
显示剩余4条评论

40

这篇讨论帖子虽然有点旧,但我想使用https://github.com/mikeal/request包来做同样的事情。

这里是一个可用的示例。

var fs      = require('fs');
var request = require('request');
// Or with cookies
// var request = require('request').defaults({jar: true});

request.get({url: 'https://someurl/somefile.torrent', encoding: 'binary'}, function (err, response, body) {
  fs.writeFile("/tmp/test.torrent", body, 'binary', function(err) {
    if(err)
      console.log(err);
    else
      console.log("The file was saved!");
  }); 
});

5
文档现在建议将编码设置为null以处理二进制数据,否则会被视为utf8:https://github.com/request/request。"encoding - 用于设置响应数据 setEncoding 的编码。若为null,则返回的内容是一个Buffer对象。如果是其他值(包括默认值 undefined),则将其作为编码参数传递给toString()方法(这意味着默认情况下实际上是utf8)。 (注意:如果你希望处理二进制数据,你应该将 encoding 设置为 null。)" - Mike Cheel
我确认,你现在应该设置{ encoding: null } - m4tm4t

27

我建议使用 http-request,这样可以管理重定向。

var http = require('http-request');
var options = {url: 'http://localhost/foo.pdf'};
http.get(options, '/path/to/foo.pdf', function (error, result) {
    if (error) {
        console.error(error);
    } else {
        console.log('File downloaded at: ' + result.file);
    }
});

2
不得不承认,这是一个HTTP下载可以变得多么简单直接。 - Tracker1
显然,http-get已经到达了其生命周期的终点,它的后继者是http-request。 - Deniz Ozger
确实,它似乎完全支持向后兼容。 - Drasill
1
这是一个很好的解决方案,但需要考虑到http-request在某些情况下,比如在Node Web Kit(nwjs)中需要本地模块(C静态库),因此如果将其部署到其他不同于服务器环境的地方,则必须根据架构进行编译。 - loretoparisi

6
这个怎么样?
var http = require('http'), 
fs = require('fs'), 
options;

options = {
    host: 'www.google.com' , 
    port: 80,
    path: '/images/logos/ps_logo2.png'
}

var request = http.get(options, function(res){

//var imagedata = ''
//res.setEncoding('binary')

var chunks = [];

res.on('data', function(chunk){

    //imagedata += chunk
    chunks.push(chunk)

})

res.on('end', function(){

    //fs.writeFile('logo.png', imagedata, 'binary', function(err){

    var buffer = Buffer.concat(chunks)
    fs.writeFile('logo.png', buffer, function(err){
        if (err) throw err
        console.log('File saved.')
    })

})

如果URL不正确怎么办?我保存了<!DOCTYPE html><html lang=en> <meta charset=utf-8> <meta name=viewport content="initial-scale=1, minimum-scale=1, width=device-width"> <title>Error 404 (Not Found)!!1</title> <style> - Marcello DeSales
@yuquin,如何使用代码保存这张图片 eteacher.me/assets/images/eteacher.png - namal

3

使用request最清晰的本地保存图片的方法:

const request = require('request');
request('http://link/to/your/image/file.png').pipe(fs.createWriteStream('fileName.png'))

如果您需要在头部添加身份验证令牌,请按照以下步骤进行操作:
const request = require('request');
request({
        url: 'http://link/to/your/image/file.png',
        headers: {
            "X-Token-Auth": TOKEN,
        }
    }).pipe(fs.createWriteStream('filename.png'))                    

0

我有一个更简单的解决方案,使用fs.readFileSync(./my_local_image_path.jpg)

这适用于从Azure认知服务视觉API读取图像

const subscriptionKey = 'your_azure_subscrition_key';
const uriBase = // **MUST change your location (mine is 'eastus')**
    'https://eastus.api.cognitive.microsoft.com/vision/v2.0/analyze';

// Request parameters.
const params = {
    'visualFeatures': 'Categories,Description,Adult,Faces',
    'maxCandidates': '2',
    'details': 'Celebrities,Landmarks',
    'language': 'en'
};

const options = {
    uri: uriBase,
    qs: params,
    body: fs.readFileSync(./my_local_image_path.jpg),
    headers: {
        'Content-Type': 'application/octet-stream',
        'Ocp-Apim-Subscription-Key' : subscriptionKey
    }
};

request.post(options, (error, response, body) => {
if (error) {
    console.log('Error: ', error);
    return;
}
let jsonString = JSON.stringify(JSON.parse(body), null, '  ');
body = JSON.parse(body);
if (body.code) // err
{
    console.log("AZURE: " + body.message)
}

console.log('Response\n' + jsonString);

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接