简体   繁体   English

如何使用 Node.js 下载文件(不使用第三方库)?

[英]How to download a file with Node.js (without using third-party libraries)?

How do I download a file with Node.js without using third-party libraries ?如何在不使用第三方库的情况下下载带有 Node.js 的文件?

I don't need anything special.我不需要什么特别的。 I only want to download a file from a given URL, and then save it to a given directory.我只想从给定的 URL 下载文件,然后将其保存到给定目录。

You can create an HTTP GET request and pipe its response into a writable file stream:您可以创建 HTTP GET请求并将其response通过管道传输到可写文件流中:

const http = require('http'); // or 'https' for https:// URLs
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);

   // after download completed close filestream
   file.on("finish", () => {
       file.close();
       console.log("Download Completed");
   });
});

If you want to support gathering information on the command line--like specifying a target file or directory, or URL--check out something like Commander .如果您想支持在命令行上收集信息——比如指定目标文件或目录,或 URL——请查看Commander之类的内容。

More detailed explanation in https://sebhastian.com/nodejs-download-file/更详细的解释在https://sebhastian.com/nodejs-download-file/

Don't forget to handle errors!不要忘记处理错误! The following code is based on Augusto Roman's answer.以下代码基于 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);
  });
};

As Michelle Tilley said, but with the appropriate control flow:正如 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);
    });
  });
}

Without waiting for the finish event, naive scripts may end up with an incomplete file.如果不等待finish事件,天真的脚本可能会以不完整的文件结束。

Edit: Thanks to @Augusto Roman for pointing out that cb should be passed to file.close , not called explicitly.编辑:感谢@Augusto Roman 指出cb应该传递给file.close ,而不是显式调用。

Speaking of handling errors, it's even better listening to request errors too.说到处理错误,听请求错误也更好。 I'd even validate by checking response code.我什至会通过检查响应代码来验证。 Here it's considered success only for 200 response code, but other codes might be good.此处仅对 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, () => cb(err.message)); // delete the (partial) file and then return the error
    });

    file.on('error', (err) => { // Handle errors
        fs.unlink(dest, () => cb(err.message)); // delete the (partial) file and then return the error
    });
};

Despite the relative simplicity of this code, I would advise to use the request module as it handles many more protocols (hello HTTPS!) which aren't natively supported by http .尽管这段代码相对简单,但我还是建议使用request 模块,因为它可以处理更多http本身不支持的协议(你好 HTTPS!)。

That would be done like so:可以这样做:

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, () => cb(err.message)); // delete the (partial) file and then return the error
    });

    file.on('error', (err) => { // Handle errors
        fs.unlink(dest, () => cb(err.message)); // delete the (partial) file and then return the error
    });
};

EDIT :编辑

To make it work with https , change要使其与https一起使用,请更改

const http = require('http');

to

const http = require('https');

gfxmonk's answer has a very tight data race between the callback and the file.close() completing. gfxmonk 的答案在回调和file.close()完成之间存在非常紧张的数据竞争。 file.close() actually takes a callback that is called when the close has completed. file.close()实际上接受一个在关闭完成时调用的回调。 Otherwise, immediate uses of the file may fail (very rarely!).否则,文件的立即使用可能会失败(非常罕见!)。

A complete solution is:一个完整的解决方案是:

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

Without waiting for the finish event, naive scripts may end up with an incomplete file.如果不等待完成事件,天真的脚本可能会以不完整的文件结束。 Without scheduling the cb callback via close, you may get a race between accessing the file and the file actually being ready.如果不通过 close 安排cb回调,您可能会在访问文件和实际准备好的文件之间发生竞争。

Maybe node.js has changed, but it seems there are some problems with the other solutions (using node v8.1.2):也许 node.js 发生了变化,但其他解决方案似乎存在一些问题(使用 node v8.1.2):

  1. You don't need to call file.close() in the finish event.您不需要在finish事件中调用file.close() Per default the fs.createWriteStream is set to autoClose: https://nodejs.org/api/fs.html#fs_fs_createwritestream_path_options默认情况下, fs.createWriteStream设置为 autoClose: https ://nodejs.org/api/fs.html#fs_fs_createwritestream_path_options
  2. file.close() should be called on error.错误时应该调用file.close() 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删除文件( unlink() )时可能不需要这样做,但通常是: https ://nodejs.org/api/stream.html#stream_readable_pipe_destination_options
  3. Temp file is not deleted on statusCode !== 200临时文件未在statusCode !== 200
  4. fs.unlink() without a callback is deprecated (outputs warning)不推荐使用没有回调的fs.unlink() (输出警告)
  5. If dest file exists;如果dest文件存在; it is overridden它被覆盖

Below is a modified solution (using ES6 and promises) which handles these problems.下面是一个修改过的解决方案(使用 ES6 和 Promise)来处理这些问题。

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

for those who came in search of es6-style promise based way, I guess it would be something like:对于那些寻找基于 es6 风格的 Promise 方式的人来说,我想它会是这样的:

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

Based on the other answers above and some subtle issues, here is my attempt.基于上面的其他答案和一些微妙的问题,这是我的尝试。

  1. Check the file does not exist before hitting the network by using fs.access .使用fs.access在访问网络之前检查文件不存在。
  2. Only create the fs.createWriteStream if you get a 200 OK status code.只有在获得200 OK状态码时才创建fs.createWriteStream This reduces the amount of fs.unlink commands required to tidy up temporary file handles.这减少了整理临时文件句柄所需的fs.unlink命令的数量。
  3. Even on a 200 OK we can still possibly reject due to an EEXIST file already exists (imagine another process created the file whilst we were doing network calls).即使在200 OK时,我们仍然可能reject ,因为EEXIST文件已经存在(想象另一个进程在我们进行网络调用时创建了该文件)。
  4. Recursively call download if you get a 301 Moved Permanently or 302 Found (Moved Temporarily) redirect following the link location provided in the header.如果您在标题中提供的链接位置后获得301 Moved Permanently302 Found (Moved Temporarily)重定向,则递归调用download
  5. The issue with some of the other answers recursively calling download was that they called resolve(download) instead of download(...).then(() => resolve()) so the Promise would return before the download actually finished.递归调用download的其他一些答案的问题在于它们调用了resolve(download)而不是download(...).then(() => resolve()) ,因此Promise会在下载实际完成之前返回。 This way the nested chain of promises resolve in the correct order.这样,嵌套的承诺链以正确的顺序解析。
  6. It might seem cool to clean up the temp file asynchronously, but I chose to reject only after that completed too so I know that everything start to finish is done when this promise resolves or rejects.异步清理临时文件似乎很酷,但我也选择在完成后才拒绝,所以我知道当这个承诺解决或拒绝时,一切都开始完成。
const https = require('https');
const fs = require('fs');

/**
 * Download a resource from `url` to `dest`.
 * @param {string} url - Valid URL to attempt download of resource
 * @param {string} dest - Valid path to save the file.
 * @returns {Promise<void>} - Returns asynchronously when successfully completed download
 */
function download(url, dest) {
  return new Promise((resolve, reject) => {
    // Check file does not exist yet before hitting network
    fs.access(dest, fs.constants.F_OK, (err) => {

        if (err === null) reject('File already exists');

        const request = https.get(url, response => {
            if (response.statusCode === 200) {
       
              const file = fs.createWriteStream(dest, { flags: 'wx' });
              file.on('finish', () => resolve());
              file.on('error', err => {
                file.close();
                if (err.code === 'EEXIST') reject('File already exists');
                else fs.unlink(dest, () => reject(err.message)); // Delete temp file
              });
              response.pipe(file);
            } else if (response.statusCode === 302 || response.statusCode === 301) {
              //Recursively follow redirects, only a 200 will resolve.
              download(response.headers.location, dest).then(() => resolve());
            } else {
              reject(`Server responded with ${response.statusCode}: ${response.statusMessage}`);
            }
          });
      
          request.on('error', err => {
            reject(err.message);
          });
    });
  });
}

Solution with timeout, prevent memory leak :超时解决方案,防止内存泄漏:

The following code is based on Brandon Tilley's answer :以下代码基于 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();
    });
});

Don't make file when you get an error, and prefere to use timeout to close your request after X secondes.遇到错误时不要生成文件,并且更喜欢在 X 秒后使用超时来关闭您的请求。

Hi,I think you can use child_process module and curl command.你好,我想你可以使用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()

In addition,when you want download large、multiple files,you can use cluster module to use more cpu cores.另外,当你想下载大、多文件时,可以使用集群模块来使用更多的cpu核心。

Modern version (ES6, Promise, Node 12.x+ ) works for https/http.现代版本(ES6、Promise、Node 12.x+)适用于 https/http。 ALso it supports redirects 302 & 301. I decided do not use 3rd party libraries due to it can be easy done with standard Node.js libs.它还支持重定向 302 和 301。我决定不使用 3rd 方库,因为它可以使用标准 Node.js 库轻松完成。

// download.js
import fs from 'fs'
import https from 'https'
import http from 'http'
import { basename } from 'path'
import { URL } from 'url'

const TIMEOUT = 10000

function download (url, dest) {
  const uri = new URL(url)
  if (!dest) {
    dest = basename(uri.pathname)
  }
  const pkg = url.toLowerCase().startsWith('https:') ? https : http

  return new Promise((resolve, reject) => {
    const request = pkg.get(uri.href).on('response', (res) => {
      if (res.statusCode === 200) {
        const file = fs.createWriteStream(dest, { flags: 'wx' })
        res
          .on('end', () => {
            file.end()
            // console.log(`${uri.pathname} downloaded to: ${path}`)
            resolve()
          })
          .on('error', (err) => {
            file.destroy()
            fs.unlink(dest, () => reject(err))
          }).pipe(file)
      } else if (res.statusCode === 302 || res.statusCode === 301) {
        // Recursively follow redirects, only a 200 will resolve.
        download(res.headers.location, dest).then(() => resolve())
      } else {
        reject(new Error(`Download request failed, response status: ${res.statusCode} ${res.statusMessage}`))
      }
    })
    request.setTimeout(TIMEOUT, function () {
      request.abort()
      reject(new Error(`Request timeout after ${TIMEOUT / 1000.0}s`))
    })
  })
}

export default download

Kudo to Andrey Tkachenko for his gist which I modified我修改了Andrey Tkachenko要点

Include it in another file and use将其包含在另一个文件中并使用

const download = require('./download.js')
const url = 'https://raw.githubusercontent.com/replace-this-with-your-remote-file'
console.log('Downloading ' + url)

async function run() {
  console.log('Downloading file')
  try {
    await download(url, 'server')
    console.log('Download done')
  } catch (e) {
    console.log('Download failed')
    console.log(e.message)
  }
}

run()

Vince Yuan's code is great but it seems to be something wrong. Vince Yuan 的代码很棒,但似乎有问题。

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

I prefer request() because you can use both http and https with it.我更喜欢 request() 因为你可以同时使用 http 和 https 。

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

✅So if you use pipeline , it would close all other streams and make sure that there are no memory leaks. ✅因此,如果您使用管道,它将关闭所有其他流并确保没有内存泄漏。

Working example:工作示例:

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

From my answer to "What's the difference between .pipe and .pipeline on streams" .“流中 .pipe 和 .pipeline 有什么区别”的回答开始。

You can use https://github.com/douzi8/ajax-request#download您可以使用https://github.com/douzi8/ajax-request#download

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

Download using promise, which resolve a readable stream.使用 promise 下载,它解析一个可读流。 put extra logic to handle the redirect.放置额外的逻辑来处理重定向。

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

Using the http2 Module使用 http2 模块

I saw answers using the http , https , and request modules.我看到了使用httphttpsrequest模块的答案。 I'd like to add one using yet another native NodeJS module that supports either the http or https protocol:我想使用另一个支持 http 或 https 协议的本机 NodeJS 模块添加一个:

Solution解决方案

I've referenced the official NodeJS API, as well as some of the other answers on this question for something I'm doing.对于我正在做的事情,我参考了官方的 NodeJS API,以及关于这个问题的其他一些答案。 The following was the test I wrote to try it out, which worked as intended:以下是我为尝试而编写的测试,它按预期工作:

import * as fs from 'fs';
import * as _path from 'path';
import * as http2 from 'http2';

/* ... */

async function download( host, query, destination )
{
    return new Promise
    (
        ( resolve, reject ) =>
        {
            // Connect to client:
            const client = http2.connect( host );
            client.on( 'error', error => reject( error ) );

            // Prepare a write stream:
            const fullPath = _path.join( fs.realPathSync( '.' ), destination );
            const file = fs.createWriteStream( fullPath, { flags: "wx" } );
            file.on( 'error', error => reject( error ) );

            // Create a request:
            const request = client.request( { [':path']: query } );

            // On initial response handle non-success (!== 200) status error:
            request.on
            (
                'response',
                ( headers/*, flags*/ ) =>
                {
                    if( headers[':status'] !== 200 )
                    {
                        file.close();
                        fs.unlink( fullPath, () => {} );
                        reject( new Error( `Server responded with ${headers[':status']}` ) );
                    }
                }
            );

            // Set encoding for the payload:
            request.setEncoding( 'utf8' );

            // Write the payload to file:
            request.on( 'data', chunk => file.write( chunk ) );

            // Handle ending the request
            request.on
            (
                'end',
                () =>
                {
                    file.close();
                    client.close();
                    resolve( { result: true } );
                }
            );

            /* 
                You can use request.setTimeout( 12000, () => {} ) for aborting
                after period of inactivity
            */

            // Fire off [flush] the request:
            request.end();
        }
    );
}

Then, for example:然后,例如:

/* ... */

let downloaded = await download( 'https://gitlab.com', '/api/v4/...', 'tmp/tmpFile' );

if( downloaded.result )
{
    // Success!
}

// ...

External References外部参考

EDIT Information编辑信息

  • The solution was written for typescript, the function a class method - but with out noting this the solution would not have worked for the presumed javascript user with out proper use of the function declaration, which our contributor has so promptly added.该解决方案是为 typescript 编写的,该函数是一个类方法- 但如果不注意这一点,如果没有正确使用我们的贡献者已迅速添加的function声明,该解决方案将不适用于假定的 javascript 用户。 Thanks!谢谢!

download.js (ie /project/utils/download.js) 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应用程序.js

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

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

If you are using express use res.download() method.如果您使用的是 express 使用 res.download() 方法。 otherwise fs module use.否则 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");
          }
        });  
    }

Path : img type : jpg random uniqid路径:img 类型:jpg 随机 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;
}

Without library it could be buggy just to point out.如果没有库,只是指出它可能是错误的。 Here are a few:这里有几个:

Here my suggestion:这是我的建议:

  • Call system tool like wget or curl调用wgetcurl等系统工具
  • use some tool like node-wget-promise which also very simple to use.使用一些工具,比如node-wget-promise ,它也很容易使用。 var wget = require('node-wget-promise'); wget('http://nodejs.org/images/logo.svg');

Writing my own solution since the existing didn't fit my requirements.编写我自己的解决方案,因为现有的不符合我的要求。

What this covers:这包括什么:

  • HTTPS download (switch package to http for HTTP downloads) HTTPS 下载(将包切换到http以进行 HTTP 下载)
  • Promise based function基于承诺的功能
  • Handle forwarded path (status 302)处理转发路径(状态 302)
  • Browser header - required on a few CDNs浏览器标头 - 一些 CDN 需要
  • Filename from URL (as well as hardcoded)来自 URL 的文件名(以及硬编码)
  • Error handling错误处理

It's typed, it's safer.它是打字的,它更安全。 Feel free to drop the types if you're working with plain JS (no Flow, no TS) or convert to a .d.ts file如果您使用纯 JS(无 Flow,无 TS)或转换为.d.ts文件,请随意删除类型

index.js index.js

import httpsDownload from httpsDownload;
httpsDownload('https://example.com/file.zip', './');

httpsDownload.[js|ts] https下载。[js|ts]

import https from "https";
import fs from "fs";
import path from "path";

function download(
  url: string,
  folder?: string,
  filename?: string
): Promise<void> {
  return new Promise((resolve, reject) => {
    const req = https
      .request(url, { headers: { "User-Agent": "javascript" } }, (response) => {
        if (response.statusCode === 302 && response.headers.location != null) {
          download(
            buildNextUrl(url, response.headers.location),
            folder,
            filename
          )
            .then(resolve)
            .catch(reject);
          return;
        }

        const file = fs.createWriteStream(
          buildDestinationPath(url, folder, filename)
        );
        response.pipe(file);
        file.on("finish", () => {
          file.close();
          resolve();
        });
      })
      .on("error", reject);
    req.end();
  });
}

function buildNextUrl(current: string, next: string) {
  const isNextUrlAbsolute = RegExp("^(?:[a-z]+:)?//").test(next);
  if (isNextUrlAbsolute) {
    return next;
  } else {
    const currentURL = new URL(current);
    const fullHost = `${currentURL.protocol}//${currentURL.hostname}${
      currentURL.port ? ":" + currentURL.port : ""
    }`;
    return `${fullHost}${next}`;
  }
}

function buildDestinationPath(url: string, folder?: string, filename?: string) {
  return path.join(folder ?? "./", filename ?? generateFilenameFromPath(url));
}

function generateFilenameFromPath(url: string): string {
  const urlParts = url.split("/");
  return urlParts[urlParts.length - 1] ?? "";
}

export default download;
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);
  });
};
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');
});

Here's yet another way to handle it without 3rd party dependency and also searching for redirects:这是另一种在没有第三方依赖和搜索重定向的情况下处理它的方法:

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

For Node with Promise support, a simple Node shim for (part of the) Fetch API requires only a smattering of extra code compared to other answers:对于支持Promise的节点,与其他答案相比,用于(部分)获取 API的简单节点填充程序只需要少量额外代码:

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

Which you can then use to whatever you need using normal fetch syntax:然后,您可以使用正常的 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.

You can try using res.redirect to the https file download url, and then it will be downloading the file.您可以尝试使用res.redirect到 https 文件下载 url,然后它将下载文件。

Like: res.redirect('https//static.file.com/file.txt');比如: res.redirect('https//static.file.com/file.txt');

I've found this approach to be the most helpful especially when it comes to pdfs and random other files.我发现这种方法最有帮助,尤其是在涉及 pdf 和随机其他文件时。

import fs from "fs";

  fs.appendFile("output_file_name.ext", fileDataInBytes, (err) => {
    if (err) throw err;
    console.log("File saved!");
  });

我们可以使用下载节点模块,它非常简单,请参考下面的https://www.npmjs.com/package/download

I suggest you to use res.download same as follow:我建议你使用res.download如下:

app.get('/download', function(req, res){
  const file = `${__dirname}/folder/abc.csv`;
  res.download(file); // Set disposition and send it.
});
var requestModule=require("request");

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 在Node.js中使用第三方JavaScript文件 - Using third-party JavaScript file in Node.js 如何在不使用第三方库的情况下使用 Node.js 读取 PDF 文件? - How to read PDF files with Node.js without using third-party library? Hyperledger Composer事务可以支持第三方javascript库/node.js包吗? - Can Hyperledger Composer transactions support third-party javascript libraries/node.js packages? 如何在没有第三方库的情况下在浏览器上加密消息并在节点服务器上解密? - How to encrypt message on browser and decrypt on node server without third-party libraries? 在Mozilla附加SDK中使用第三方JS库 - Using third-party JS libraries in Mozilla Add-On SDK node.js - PM2将未捕获的异常记录到第三方服务(作为Logentries) - node.js - PM2 log uncaught exceptions to third-party service (as Logentries) 使用 Node.js 监控第三方流量的最佳方式是什么? - What's the best way to monitor third-party traffic with Node.js? 如何在用户脚本中集成第三方JavaScript库 - how to integrate third-party JavaScript libraries in userscripts 工兵 如何在全球范围内使用第三方库 - Sapper. How to globally use third-party libraries 使用 JavaScript 进行人脸锁定,无需使用任何第三方框架,包括 TensorFlow.js - Face lock using JavaScript without using any third-party frameworks including TensorFlow.js
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM