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

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

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



답변

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 와 같은 것을 확인하십시오 .


답변

오류를 처리하는 것을 잊지 마십시오! 다음 코드는 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);
  });
};


답변

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.


답변

오류 처리와 관련하여 오류를 요청하는 것이 더 좋습니다. 응답 코드를 확인하여 유효성을 검사합니다. 여기서는 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);
    });
};


답변

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닫기를 통해 콜백을 예약하지 않으면 파일에 액세스하는 것과 실제로 준비중인 파일간에 경쟁이 발생할 수 있습니다.


답변

아마 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);
            }
        });
    });
}


답변

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

다음 코드는 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 초 후에 시간 초과를 사용하여 요청을 닫는 것을 선호합니다.