如何在不使用第三方库的情况下使用Node.js下载文件?

我不需要什么特别的东西。我只想从给定的URL下载文件,然后将其保存到给定的目录。


当前回答

对于那些寻找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));

其他回答

也许node.js已经改变了,但似乎其他解决方案(使用node v8.1.2)有一些问题:

You don't need to call file.close() in the finish event. Per default the fs.createWriteStream is set to autoClose: https://nodejs.org/api/fs.html#fs_fs_createwritestream_path_options file.close() should be called on error. Maybe this is not needed when the file is deleted (unlink()), but normally it is: https://nodejs.org/api/stream.html#stream_readable_pipe_destination_options Temp file is not deleted on statusCode !== 200 fs.unlink() without a callback is deprecated (outputs warning) If dest file exists; it is overridden

下面是一个修改后的解决方案(使用ES6和promises),它可以处理这些问题。

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

你可以使用https://github.com/douzi8/ajax-request#download

request.download('http://res.m.ctrip.com/html5/Content/images/57.png', 
  function(err, res, body) {}
);

没有库,它可能是错误的,只是指出。以下是一些例子:

不能处理http重定向,比如这个url https://calibre-ebook.com/dist/portable,它是二进制的。 http模块不能https url,你将得到协议“https:”不支持。

以下是我的建议:

调用系统工具,如wget或curl 使用一些类似node-wget-promise的工具,使用起来也非常简单。 Var wget = require('node-wget-promise'); wget(“http://nodejs.org/images/logo.svg”);

2022年底编辑:

Node v18及以上版本自带自带的Fetch API支持。使用它。

最初的回答:

对于支持承诺的节点,与其他答案相比,一个简单的(部分)Fetch API的Node shim只需要少量额外的代码:

const fs = require(`fs`);
const http = require(`http`);
const https = require(`https`);

module.exports = function fetch(url) {
  return new Promise((resolve, reject) => {
    const data = [];
    const client = url.startsWith("https") ? https : http;
    client
      .request(url, (res) => {
        res.on(`data`, (chunk) => data.push(chunk));
        res.on(`end`, () => {
          const asBytes = Buffer.concat(data);
          const asString = asBytes.toString(`utf8`);
          resolve({
            arrayBuffer: async () => asBytes,
            json: async () => JSON.parse(asString),
            text: async () => asString,
          });
        });
        res.on(`error`, (e) => reject(e));
      })
      .end();
  });
};

你可以用它来做任何你需要的事情,使用普通的fetch语法:

const fetch = require(`./tiny-fetch.js`);

fetch(`https://placekitten.com/200/300`)
  .then(res => res.arrayBuffer())
  .then(bytes => fs.writeFileSync(`kitten.jpg`, bytes))
  .catch(e => console.error(e));

fetch(`https://jsonplaceholder.typicode.com/todos/1`)
  .then(res => res.json())
  .then(obj => console.log(obj))
  .catch(e => console.error(e));

// etc.

如果您正在使用express,请使用res.download()方法。否则使用fs模块。

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

(or)

   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");
          }
        });  
    }