ccforward / cc

Code & Blog
1.59k stars 193 forks source link

22.远程文件批量下载 Node.js & Ruby #23

Open ccforward opened 9 years ago

ccforward commented 9 years ago

远程文件批量下载 Node.js & Ruby

var fs = require("fs");
var http = require('http');

fs.readFile('img.txt', 'UTF-8', function(err, data){
    if(!err){
        var con = data.split('\n');
        var i=0,
            len=con.length;

        function down(file){
            http.get(file, function(res){
                var imgData = '';
                res.setEncoding('binary');

                res.on('data', function(chunk){
                    imgData+=chunk;
                });
                res.on('end', function(){
                    fs.writeFile(i+'.jpg', imgData, 'binary', function(err){
                        if(err){
                            console.log('fail: ' + file);
                        }
                        i++;
                        if(i<len){
                            down(con[i]);
                        }
                        console.log('download over')
                    });
                });
            });
        }
        down(con[0]);
    }else {
        console.log(err);
    }
});

关于 Node 这段代码最初文件写入用的 for 循环,没有把 down 函数抽取出来做递归,于是每次做文件写操作时候会覆盖上一个文件,是因为 javascript 是单线程的,先执行完整个循环的同步内容之后才去执行其中的异步操作。get 函数里的匿名函数就是一个异步回调。

处于闭包原则,该函数会保留 for 循环最后一次循环的i变量,才会导致只保存了一个文件。

所以写 node 时候尽可能的多用递归。

但是因为异步所以会按顺序依次下载,速度并不是很快,依次如下:

for 循环中如果使用自执行函数 (function(i,file){})(i,con[i]) 就可以那个避免上述问题。

更重要的是,异步回调速度很快,因为不会产生阻塞,可以同步的下载图片,所以最终我选择的代码如下:

var fs = require("fs");
var http = require('http');

fs.readFile('img.txt', 'UTF-8', function(err, data){
    if(!err){
        var con = data.split('\n');
        for(var i=0,len=con.length;i<len;i++){
            (function(i, file){
                http.get(file, function(res){
                    var imgData = '';
                    res.setEncoding('binary');

                    res.on('data', function(chunk){
                        imgData+=chunk;
                    });
                    res.on('end', function(){
                        fs.writeFile(i+'.jpg', imgData, 'binary', function(err){
                            if(err){
                                console.log('fail: ' + con[0]);
                            }
                            console.log('download over')
                        });
                    });
                });
            })(i, con[i]);
        }
    }else {
        console.log(err);
    }
});

Ruby 版本

最近看了点 Ruby 的代码,实现相同功能只需要17行,简单暴力

还没有研究 Ruby 是否可以做异步回调

require 'net/http'
require 'open-uri'

con = Array.new
File.open("img.txt", "r") do |file|
    while line  = file.gets
       con.push(line)
    end
end

name = 0
for i in con do
    data = open(i){|f|f.read}
    open(name.to_s + '.jpg', 'wb'){|f|f.write(data)}
    name = name +1
    print name
end