Node.js로 파일을 다운로드하는 방법 (타사 라이브러리를 사용하지 않고)?


443

타사 라이브러리를 사용하지 않고 Node.js로 파일을 어떻게 다운로드 합니까?

나는 특별한 것이 필요하지 않습니다. 주어진 URL에서 파일을 다운로드 한 다음 주어진 디렉토리에 저장하고 싶습니다.


5
"node.js로 파일 다운로드" - 서버에 업로드 하시겠습니까? 또는 서버를 사용하여 원격 서버에서 파일을 검색합니까? 또는 node.js 서버에서 다운로드하기 위해 파일을 클라이언트에 제공합니까?
요셉

66
"나는 주어진 URL에서 파일을 다운로드 한 다음 주어진 디렉토리에 저장하고 싶다"고 생각한다. :)
Michelle Tilley

34
요셉은 모든 노드 프로세스가 서버 프로세스 있다는 잘못된 주장을하고있다
lededje

1
@lededje 서버 프로세스가 파일을 다운로드하여 서버의 디렉토리에 저장하지 못하게하는 요인은 무엇입니까? 현명하게 할 수 있습니다.
Gherman

답변:


598

HTTP GET요청을 작성하여 response쓰기 가능한 파일 스트림으로 파이프 할 수 있습니다 .

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

const file = fs.createWriteStream("file.jpg");
const request = http.get("http://i3.ytimg.com/vi/J---aiyznGQ/mqdefault.jpg", function(response) {
  response.pipe(file);
});

대상 파일이나 디렉토리 또는 URL 지정과 같이 명령 행에서 정보 수집을 지원하려면 Commander 와 같은 것을 확인하십시오 .


3
이 스크립트를 실행할 때 다음과 같은 콘솔 출력이 나타납니다 node.js:201 throw e; // process.nextTick error, or 'error' event on first tick ^ Error: connect ECONNREFUSED at errnoException (net.js:646:11) at Object.afterConnect [as oncomplete] (net.js:637:18) .
앤더슨 그린

http.get회선 에 다른 URL을 사용해보십시오 . 어쩌면 http://i3.ytimg.com/vi/J---aiyznGQ/mqdefault.jpg(및 교체 file.png와 함께 file.jpg).
Michelle Tilley

8
이 코드는 스크립트가 종료되거나 데이터가 손실 될 때 파일을 올바르게 닫습니까?
philk

2
@quantumpotato 귀하의 요청에 대한 답변을 확인하십시오.
Michelle Tilley

6
당신이 요청하는 경우는 REQ의 URL 유형에 따라 https사용합니다 https이 오류가 발생합니다 그렇지.
Krishnadas PC

523

오류를 처리하는 것을 잊지 마십시오! 다음 코드는 Augusto Roman의 답변을 기반으로합니다.

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

var download = function(url, dest, cb) {
  var file = fs.createWriteStream(dest);
  var request = http.get(url, function(response) {
    response.pipe(file);
    file.on('finish', function() {
      file.close(cb);  // close() is async, call cb after close completes.
    });
  }).on('error', function(err) { // Handle errors
    fs.unlink(dest); // Delete the file async. (But we don't check the result)
    if (cb) cb(err.message);
  });
};

2
@ vince-yuan download()자체가 pipe가능합니까?
rasx

@theGrayFox이 답변의 코드가 허용되는 코드보다 길기 때문입니다. :)
pootow

2
@Abdul node.js / javascript를 처음 접하는 것 같습니다. 이 자습서를 살펴보십시오 : tutorialspoint.com/nodejs/nodejs_callbacks_concept.htm 복잡하지 않습니다.
빈스 위안

1
@ Abdul 어쩌면 당신이 알아 낸 것을 나머지 클래스와 공유한다면 좋을 것입니까?
Curtwagner1984

5
다운로드 속도를 볼 수있는 방법이 있습니까? 얼마나 많은 mb / s를 추적 할 수 있습니까? 감사!
Tino Caer

137

Michelle Tilley가 말했듯이 적절한 제어 흐름이 있습니다.

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

var download = function(url, dest, cb) {
  var file = fs.createWriteStream(dest);
  http.get(url, function(response) {
    response.pipe(file);
    file.on('finish', function() {
      file.close(cb);
    });
  });
}

finish이벤트를 기다리지 않고 순진 스크립트는 불완전한 파일로 끝날 수 있습니다.

편집 : @Augusto Roman에게 명시 적으로 호출되지 않고에 cb전달해야한다고 지적 해 주셔서 감사합니다 file.close.


3
콜백은 나를 혼란스럽게합니다. 내가 지금 호출 download()하면 어떻게해야합니까? 나는 cb논쟁 으로 무엇을 놓을 것인가? 나는 가지고 download('someURI', '/some/destination', cb)있지만 cb에 넣을 것을 이해하지 못한다
Abdul

1
@Abdul 파일을 성공적으로 가져 왔을 때 무언가를해야하는 경우에만 함수를 사용하여 콜백을 지정합니다.
CatalinBerta 10

65

오류 처리와 관련하여 오류를 요청하는 것이 더 좋습니다. 응답 코드를 확인하여 유효성을 검사합니다. 여기서는 200 응답 코드에 대해서만 성공한 것으로 간주되지만 다른 코드는 좋습니다.

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

const download = (url, dest, cb) => {
    const file = fs.createWriteStream(dest);

    const request = http.get(url, (response) => {
        // check if response is success
        if (response.statusCode !== 200) {
            return cb('Response status was ' + response.statusCode);
        }

        response.pipe(file);
    });

    // close() is async, call cb after close completes
    file.on('finish', () => file.close(cb));

    // check for request error too
    request.on('error', (err) => {
        fs.unlink(dest);
        return cb(err.message);
    });

    file.on('error', (err) => { // Handle errors
        fs.unlink(dest); // Delete the file async. (But we don't check the result) 
        return cb(err.message);
    });
};

이 코드의 상대적 단순성에도 불구하고 요청 모듈 을 기본적으로 지원하지 않는 더 많은 프로토콜 (hello HTTPS!)을 처리 하므로 요청 모듈 을 사용하는 것이 http좋습니다.

그렇게 할 것입니다 :

const fs = require('fs');
const request = require('request');

const download = (url, dest, cb) => {
    const file = fs.createWriteStream(dest);
    const sendReq = request.get(url);

    // verify response code
    sendReq.on('response', (response) => {
        if (response.statusCode !== 200) {
            return cb('Response status was ' + response.statusCode);
        }

        sendReq.pipe(file);
    });

    // close() is async, call cb after close completes
    file.on('finish', () => file.close(cb));

    // check for request errors
    sendReq.on('error', (err) => {
        fs.unlink(dest);
        return cb(err.message);
    });

    file.on('error', (err) => { // Handle errors
        fs.unlink(dest); // Delete the file async. (But we don't check the result)
        return cb(err.message);
    });
};

2
요청 모듈은 HTTP에서 바로 작동합니다. 멋있는!
Thiago C. S Ventura

@ventura yep, btw, 보안 연결을 처리 할 수 있는 기본 https 모듈 도 있습니다.
Buzut

의심의 여지없이 오류가 발생하기 쉽습니다. 어쨌든, 요청 모듈을 사용하는 것이 옵션 인 경우에는 더 높은 수준이므로 더 쉽고 효율적이라고 조언합니다.
Buzut

2
@ 알렉스, 아니, 이것은 오류 메시지이며 반환이 있습니다. 따라서 response.statusCode !== 200cb on finish이 호출되지 않으면
Buzut

1
request 모듈을 사용한 예제를 보여 주셔서 감사합니다.
피트 알빈

48

gfxmonk의 답변은 콜백과 file.close()완료 사이의 데이터 경쟁이 매우 엄격합니다 . file.close()실제로 닫기가 완료되면 호출되는 콜백을받습니다. 그렇지 않으면 파일을 즉시 사용하지 못할 수 있습니다 (매우 드물게!).

완전한 해결책은 다음과 같습니다.

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

var download = function(url, dest, cb) {
  var file = fs.createWriteStream(dest);
  var request = http.get(url, function(response) {
    response.pipe(file);
    file.on('finish', function() {
      file.close(cb);  // close() is async, call cb after close completes.
    });
  });
}

종료 이벤트를 기다리지 않고 순진 스크립트는 불완전한 파일로 끝날 수 있습니다. cb닫기를 통해 콜백을 예약하지 않으면 파일에 액세스하는 것과 실제로 준비중인 파일간에 경쟁이 발생할 수 있습니다.


2
요청을 변수에 저장하고 있습니까?
polkovnikov.ph

그는 변수에 변수를 "저장"하므로 기본적으로 전역 변수가되지 않습니다.
philk

@ philk 전역 변수 var request =가 제거되면 어떻게 생성되는지 어떻게 알 수 있습니까?
ma11hew28

당신은 옳습니다. 요청을 저장할 필요가 없으며 어쨌든 사용되지 않습니다. 그게 무슨 뜻이야?
philk

17

아마 node.js가 변경되었지만 다른 솔루션에 문제가있는 것 같습니다 (노드 v8.1.2 사용) :

  1. 당신은 호출 할 필요는 없습니다 file.close()에서 finish이벤트입니다. 기본적 fs.createWriteStream으로이 값은 autoClose로 설정되어 있습니다 : https://nodejs.org/api/fs.html#fs_fs_createwritestream_path_options
  2. file.close()오류가 발생하면 호출해야합니다. 파일을 삭제할 때 필요하지 않을 수도 unlink()있지만 ( ) 일반적으로 다음과 같습니다. https://nodejs.org/api/stream.html#stream_readable_pipe_destination_options
  3. 임시 파일은에 삭제되지 않습니다 statusCode !== 200
  4. fs.unlink() 콜백이 없으면 더 이상 사용되지 않습니다 (출력 경고)
  5. dest파일이 존재 하면 ; 재정의되었습니다

다음은 이러한 문제를 해결하는 수정 된 솔루션 (ES6 및 약속 사용)입니다.

const http = require("http");
const fs = require("fs");

function download(url, dest) {
    return new Promise((resolve, reject) => {
        const file = fs.createWriteStream(dest, { flags: "wx" });

        const request = http.get(url, response => {
            if (response.statusCode === 200) {
                response.pipe(file);
            } else {
                file.close();
                fs.unlink(dest, () => {}); // Delete temp file
                reject(`Server responded with ${response.statusCode}: ${response.statusMessage}`);
            }
        });

        request.on("error", err => {
            file.close();
            fs.unlink(dest, () => {}); // Delete temp file
            reject(err.message);
        });

        file.on("finish", () => {
            resolve();
        });

        file.on("error", err => {
            file.close();

            if (err.code === "EEXIST") {
                reject("File already exists");
            } else {
                fs.unlink(dest, () => {}); // Delete temp file
                reject(err.message);
            }
        });
    });
}

1
이에 대한 두 가지 의견 : 1) 아마도 문자열이 아닌 오류 객체를 거부해야합니다. 2) fs.unlink는 반드시 원하지 않는 오류를 조용히 처리합니다.
Richard Nienaber

1
이것은 잘 작동합니다! URL이 HTTPS를 사용하는 경우에, 다만 대체 const https = require("https");를 위해const http = require("http");
러스

15

시간 초과가있는 솔루션, 메모리 누수 방지 :

다음 코드는 Brandon Tilley의 답변을 기반으로합니다.

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

var request = http.get("http://example12345.com/yourfile.html", function(response) {
    if (response.statusCode === 200) {
        var file = fs.createWriteStream("copy.html");
        response.pipe(file);
    }
    // Add timeout.
    request.setTimeout(12000, function () {
        request.abort();
    });
});

오류가 발생했을 때 파일을 만들지 말고 X 초 후에 시간 초과를 사용하여 요청을 닫는 것을 선호합니다.


1
이것은 파일 일 뿐이며, 다운로드 할 프로토콜이나 서버가 없습니다 ...http.get("http://example.com/yourfile.html",function(){})
mjz19910

이 답변에 메모리 누수가 있습니까 : stackoverflow.com/a/22793628/242933 ?
ma11hew28

에서했던 것처럼 시간 초과를 추가 할 수 있습니다 http.get. 파일을 다운로드하는 데 시간이 너무 오래 걸리는 경우에만 메모리 누수가 발생합니다.
A-312

13

es6 스타일의 약속 기반 방식을 찾은 사람들에게는 다음과 같은 것 같습니다.

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

function pDownload(url, dest){
  var file = fs.createWriteStream(dest);
  return new Promise((resolve, reject) => {
    var responseSent = false; // flag to make sure that response is sent only once.
    http.get(url, response => {
      response.pipe(file);
      file.on('finish', () =>{
        file.close(() => {
          if(responseSent)  return;
          responseSent = true;
          resolve();
        });
      });
    }).on('error', err => {
        if(responseSent)  return;
        responseSent = true;
        reject(err);
    });
  });
}

//example
pDownload(url, fileLocation)
  .then( ()=> console.log('downloaded file no issues...'))
  .catch( e => console.error('error while downloading', e));

2
responseSet플래그로 인해 조사 할 시간이 없었기 때문에 파일이 불완전하게 다운로드되었습니다. 오류가 발생하지 않았지만 내가 채우는 .txt 파일에는 절반의 행이 있어야했습니다. 플래그의 논리를 제거하면 문제가 해결되었습니다. 누군가 접근 방식에 문제가 있다면 지적하고 싶었습니다. 여전히 +1
Milan Velebit

6

빈스 위안의 코드는 훌륭하지만 뭔가 잘못된 것 같습니다.

function download(url, dest, callback) {
    var file = fs.createWriteStream(dest);
    var request = http.get(url, function (response) {
        response.pipe(file);
        file.on('finish', function () {
            file.close(callback); // close() is async, call callback after close completes.
        });
        file.on('error', function (err) {
            fs.unlink(dest); // Delete the file async. (But we don't check the result)
            if (callback)
                callback(err.message);
        });
    });
}

대상 폴더를 지정할 수 있습니까?

6

http와 https를 모두 사용할 수 있기 때문에 request ()를 선호합니다.

request('http://i3.ytimg.com/vi/J---aiyznGQ/mqdefault.jpg')
  .pipe(fs.createWriteStream('cat.jpg'))

요청이 더 이상 사용되지 않는 것 같습니다. github.com/request/request/issues/3142 "As of Feb 11th 2020, request is fully deprecated. No new changes are expected to land. In fact, none have landed for some time."
Michael Kubler

5
const download = (url, path) => new Promise((resolve, reject) => {
http.get(url, response => {
    const statusCode = response.statusCode;

    if (statusCode !== 200) {
        return reject('Download error!');
    }

    const writeStream = fs.createWriteStream(path);
    response.pipe(writeStream);

    writeStream.on('error', () => reject('Error writing to file!'));
    writeStream.on('finish', () => writeStream.close(resolve));
});}).catch(err => console.error(err));

5

안녕하세요, child_process 모듈과 curl 명령을 사용할 수 있다고 생각합니다 .

const cp = require('child_process');

let download = async function(uri, filename){
    let command = `curl -o ${filename}  '${uri}'`;
    let result = cp.execSync(command);
};


async function test() {
    await download('http://zhangwenning.top/20181221001417.png', './20181221001417.png')
}

test()

또한, 여러 개의 대용량 파일을 다운로드하려는 경우 클러스터 모듈을 사용하여 더 많은 CPU 코어를 사용할 수 있습니다 .



4

읽을 수있는 스트림을 해결하는 약속을 사용하여 다운로드하십시오. 리디렉션을 처리하기 위해 추가 논리를 넣습니다.

var http = require('http');
var promise = require('bluebird');
var url = require('url');
var fs = require('fs');
var assert = require('assert');

function download(option) {
    assert(option);
    if (typeof option == 'string') {
        option = url.parse(option);
    }

    return new promise(function(resolve, reject) {
        var req = http.request(option, function(res) {
            if (res.statusCode == 200) {
                resolve(res);
            } else {
                if (res.statusCode === 301 && res.headers.location) {
                    resolve(download(res.headers.location));
                } else {
                    reject(res.statusCode);
                }
            }
        })
        .on('error', function(e) {
            reject(e);
        })
        .end();
    });
}

download('http://localhost:8080/redirect')
.then(function(stream) {
    try {

        var writeStream = fs.createWriteStream('holyhigh.jpg');
        stream.pipe(writeStream);

    } catch(e) {
        console.error(e);
    }
});

1
302는 URL 리디렉션을위한 HTTP 상태 코드이므로 if 문에서 [301,302] .indexOf (res.statusCode)! == -1을 사용해야합니다.
sidanmor

질문은 : 타사 모드를 포함하지 않도록 특이
데이비드 가티

3

express를 사용하는 경우 res.download () 메소드를 사용하십시오. 그렇지 않으면 fs 모듈 사용.

app.get('/read-android', function(req, res) {
   var file = "/home/sony/Documents/docs/Android.apk";
    res.download(file) 
}); 

(또는)

   function readApp(req,res) {
      var file = req.fileName,
          filePath = "/home/sony/Documents/docs/";
      fs.exists(filePath, function(exists){
          if (exists) {     
            res.writeHead(200, {
              "Content-Type": "application/octet-stream",
              "Content-Disposition" : "attachment; filename=" + file});
            fs.createReadStream(filePath + file).pipe(res);
          } else {
            res.writeHead(400, {"Content-Type": "text/plain"});
            res.end("ERROR File does NOT Exists.ipa");
          }
        });  
    }

3

pipeline 따라서 pipeline 을 사용하면 다른 모든 스트림을 닫고 메모리 누수가 없는지 확인합니다.

작업 예 :

const http = require('http');
const { pipeline } = require('stream');
const fs = require('fs');

const file = fs.createWriteStream('./file.jpg');

http.get('http://via.placeholder.com/150/92c952', response => {
  pipeline(
    response,
    file,
    err => {
      if (err)
        console.error('Pipeline failed.', err);
      else
        console.log('Pipeline succeeded.');
    }
  );
});

에서 내 대답"스트림에 .pipe와 .pipeline의 차이는 무엇" .


2

경로 : img 유형 : jpg random uniqid

    function resim(url) {

    var http = require("http");
    var fs = require("fs");
    var sayi = Math.floor(Math.random()*10000000000);
    var uzanti = ".jpg";
    var file = fs.createWriteStream("img/"+sayi+uzanti);
    var request = http.get(url, function(response) {
  response.pipe(file);
});

        return sayi+uzanti;
}

0

라이브러리가 없으면 지적하기가 까다로울 수 있습니다. 몇 가지가 있습니다 :

  • 이 URL 인 https://calibre-ebook.com/dist/portable 과 같이 http 리디렉션을 처리 할 수 ​​없습니다 .
  • http 모듈은 URL을 https 할 수 없습니다. Protocol "https:" not supported.

여기 내 제안 :

  • wget또는 같은 시스템 도구를 호출curl
  • 사용하기 매우 쉬운 node-wget-promise 와 같은 도구 를 사용하십시오. var wget = require('node-wget-promise'); wget('http://nodejs.org/images/logo.svg');

0
function download(url, dest, cb) {

  var request = http.get(url, function (response) {

    const settings = {
      flags: 'w',
      encoding: 'utf8',
      fd: null,
      mode: 0o666,
      autoClose: true
    };

    // response.pipe(fs.createWriteStream(dest, settings));
    var file = fs.createWriteStream(dest, settings);
    response.pipe(file);

    file.on('finish', function () {
      let okMsg = {
        text: `File downloaded successfully`
      }
      cb(okMsg);
      file.end(); 
    });
  }).on('error', function (err) { // Handle errors
    fs.unlink(dest); // Delete the file async. (But we don't check the result)
    let errorMsg = {
      text: `Error in file downloadin: ${err.message}`
    }
    if (cb) cb(errorMsg);
  });
};

0

res.redirecthttps 파일 다운로드 URL을 사용해보십시오 . 그러면 파일이 다운로드됩니다.

처럼: res.redirect('https//static.file.com/file.txt');


0
var fs = require('fs'),
    request = require('request');

var download = function(uri, filename, callback){
    request.head(uri, function(err, res, body){
    console.log('content-type:', res.headers['content-type']);
    console.log('content-length:', res.headers['content-length']);
    request(uri).pipe(fs.createWriteStream(filename)).on('close', callback);

    }); 
};   

download('https://www.cryptocompare.com/media/19684/doge.png', 'icons/taskks12.png', function(){
    console.log('done');
});

0

타사 종속성없이 처리하고 리디렉션을 검색하는 또 다른 방법이 있습니다.

        var download = function(url, dest, cb) {
            var file = fs.createWriteStream(dest);
            https.get(url, function(response) {
                if ([301,302].indexOf(response.statusCode) !== -1) {
                    body = [];
                    download(response.headers.location, dest, cb);
                  }
              response.pipe(file);
              file.on('finish', function() {
                file.close(cb);  // close() is async, call cb after close completes.
              });
            });
          }

0

download.js (예 : /project/utils/download.js)

const fs = require('fs');
const request = require('request');

const download = (uri, filename, callback) => {
    request.head(uri, (err, res, body) => {
        console.log('content-type:', res.headers['content-type']);
        console.log('content-length:', res.headers['content-length']);

        request(uri).pipe(fs.createWriteStream(filename)).on('close', callback);
    });
};

module.exports = { download };


app.js

... 
// part of imports
const { download } = require('./utils/download');

...
// add this function wherever
download('https://imageurl.com', 'imagename.jpg', () => {
  console.log('done')
});


-4
var requestModule=require("request");

requestModule(filePath).pipe(fs.createWriteStream('abc.zip'));

5
코드 덤프는 일반적으로 유용하지 않으며 다운 보트 또는 삭제 될 수 있습니다. 미래 방문자를 위해 코드가 수행하는 작업을 최소한 설명하기 위해 편집 할 가치가 있습니다.
Bugs
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.