Node.js如何检查远程URL是否存在

32

如何在不下载整个文件的情况下检查URL是否存在?我使用以下代码,但它会下载整个文件。我只需要检查它是否存在。

app.get('/api/v1/urlCheck/', function (req,res) {
    var url=req.query['url'];
    var request = require('request');
    request.get(url, {timeout: 30000, json:false}, function (error, result) {
        res.send(result.body);

    });

});

感激任何帮助!


3
你可以使用HEAD请求而不是GET请求。无论你使用的模块是什么,我想它们都会提供一个简单的接口。 - user1106925
13个回答

42

试一下:

var http = require('http'),
    options = {method: 'HEAD', host: 'stackoverflow.com', port: 80, path: '/'},
    req = http.request(options, function(r) {
        console.log(JSON.stringify(r.headers));
    });
req.end();

2
截至2020年1月,请求包仅处于维护状态,不应再使用(https://nodesource.com/blog/express-going-into-maintenance-mode)。最好选择替代方案。url-exists基于请求,因此现在也已过时。 - schlicki
13
@schlicki,你将npm的请求模块和Node.js内部的内置http模块的请求函数混淆了。 - Nikolay Schamberg

24

2021更新

使用url-exist

import urlExist from 'url-exist';

const exists = await urlExist('https://google.com');

// Handle result
console.log(exists);

2020更新

request现已弃用,这也使url-exists无法使用。请使用url-exist代替。

const urlExist = require("url-exist");

(async () => {
    const exists = await urlExist("https://google.com");
    // Handle result
    console.log(exists)
})();
如果由于某种原因需要同步使用它,您可以使用url-exist-sync

2019更新

自2017年起,request和回调函数风格(来自url-exists)已不再使用。
但是,有一种解决方法。将url-exists替换为url-exist
所以,不要使用:
const urlExists = require("url-exists")

urlExists("https://google.com", (_, exists) => {
    // Handle result
    console.log(exists)
})

使用这个:

const urlExist = require("url-exist");
 
(async () => {
    const exists = await urlExist("https://google.com");
    // Handle result
    console.log(exists)
})();

原始回答(2017年)

如果您可以访问request包,您可以尝试这个方法:

const request = require("request")
const urlExists = url => new Promise((resolve, reject) => request.head(url).on("response", res => resolve(res.statusCode.toString()[0] === "2")))
urlExists("https://google.com").then(exists => console.log(exists)) // true

大多数逻辑已经由url-exists提供。


这是一个详细的回答,但似乎url-exists不能很好地处理无效或自签名证书。 我尝试了process.env["NODE_TLS_REJECT_UNAUTHORIZED"] = 0; - Semtex
许多问题似乎都围绕着解决安全证书以访问网站。如果您在Node.js中遇到此问题,请尝试在https://github.com/node-fetch/node-fetch上开启一个问题。 - Richie Bendall
1
const urlExist = require("url-exist"); 更改为 let { default: urlExist } = await import("url-exist"); 或者 import urlExist from "url-exist",并将该行代码移动到异步函数中。如果我们使用 const urlExist = require("url-exist");,则会出现“ERR_REQUIRE_ESM”错误。 - CrazyVideoGamer
无法工作:错误:捕获到'uncaughtException'!消息:必须使用导入来加载ES模块:/app/node_modules/url-exist/index.js - Eduardo Cuomo
你需要使用 import 来加载 ES 模块,就像这个 https://github.com/sindresorhus/meta/discussions/15 一样。 - Richie Bendall
显示剩余3条评论

18

谢谢!这里是一个封装在函数中的代码(更新于5/30/17,require已移至外部):

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

    exports.checkUrlExists = function (Url, callback) {
        var options = {
            method: 'HEAD',
            host: url.parse(Url).host,
            port: 80,
            path: url.parse(Url).pathname
        };
        var req = http.request(options, function (r) {
            callback( r.statusCode== 200);});
        req.end();
    }

这很快(我大约需要50毫秒,但这将取决于您的连接和服务器速度)。请注意,它也相当基础,即它不能很好地处理重定向...


1
由于require是同步的,所以您不应在函数体内使用它。 - Westy92
3
现有的资源或URL可能返回不同于200的状态,但仍然有效。 - Grzegorz Pawlik

9

只需使用url-exists npm包测试URL是否存在即可。

var urlExists = require('url-exists');

urlExists('https://www.google.com', function(err, exists) {
  console.log(exists); // true 
});

urlExists('https://www.fakeurl.notreal', function(err, exists) {
  console.log(exists); // false 
});

1
更加简洁。谢谢分享! - Sebyddd
1
有没有选项可以使用同步调用? - Ulrich Dohou
@UlrichDohou 我不这么认为。因为要检查URL是否存在,您必须向该URL发出请求,而这反过来将是异步的。如果您只想检查URL是否语义正确,可以使用RegExp进行相同的操作。 - Rakesh Soni
1
@UlrichDohou:请查看下面的答案,其中包含一个示例以使其同步。https://dev59.com/Ol8e5IYBdhLWcg3wLX8-#49182165 - Deejers
1
这个软件包实际上只包含8行代码。其他的都是测试、软件包描述、许可证、自述文件和其他内容。 - Gherman

8

看起来很多人推荐使用一个库,但是 url-exist 包含一个数据获取库的依赖项,所以这里有一个它的克隆版本,使用了所有本地的Node模块:

const http = require('http');
const { parse, URL } = require('url');

// https://github.com/sindresorhus/is-url-superb/blob/main/index.js
function isUrl(str) {
  if (typeof str !== 'string') {
    return false;
  }

  const trimmedStr = str.trim();
  if (trimmedStr.includes(' ')) {
    return false;
  }

  try {
    new URL(str); // eslint-disable-line no-new
    return true;
  } catch {
    return false;
  }
}

// https://github.com/Richienb/url-exist/blob/master/index.js
function urlExists(url) {
  return new Promise((resolve) => {
    if (!isUrl(url)) {
      resolve(false);
    }

    const options = {
      method: 'HEAD',
      host: parse(url).host,
      path: parse(url).pathname,
      port: 80,
    };

    const req = http.request(options, (res) => {
      resolve(res.statusCode < 400 || res.statusCode >= 500);
    });
    
    req.end();
  });
}

urlExists(
  'https://dev59.com/Ol8e5IYBdhLWcg3wLX8-
).then(console.log);

这也可能吸引那些不想为了一个非常简单的目的而安装依赖项的人。

6
在Node中,将require放入函数中是错误的方法。 遵循ES6方法支持所有正确的HTTP状态码,并且如果您有一个不良的“主机”(如fff.kkk),当然会检索错误。
checkUrlExists(host,cb) {
    http.request({method:'HEAD',host,port:80,path: '/'}, (r) => {
        cb(null, r.statusCode >= 200 && r.statusCode < 400 );
    }).on('error', cb).end();
}

不太确定,但这似乎是检查链接错误的最简洁方法! - peterb

4
请看一下npm包url-existshttps://www.npmjs.com/package/url-exists

设置:

$ npm install url-exists

用法:

const urlExists = require('url-exists');

urlExists('https://www.google.com', function(err, exists) {
  console.log(exists); // true 
});

urlExists('https://www.fakeurl.notreal', function(err, exists) {
  console.log(exists); // false 
});

您还可以将其转换为Promise,以利用awaitasync

const util = require('util');
const urlExists = util.promisify(require('url-exists'));

let isExists = await urlExists('https://www.google.com'); // true
isExists = await urlExists('https://www.fakeurl.notreal'); // false

愉快的编码!


2

参考其他回答,下面是一个使用 promises 的版本,同时也适用于 HTTPS URI(适用于 Node.js 6+):

const http = require('http');
const https = require('https');
const url = require('url');

const request = (opts = {}, cb) => {
  const requester = opts.protocol === 'https:' ? https : http;
  return requester.request(opts, cb);
};

module.exports = target => new Promise((resolve, reject) => {
  let uri;

  try {
    uri = url.parse(target);
  } catch (err) {
    reject(new Error(`Invalid url ${target}`));
  }

  const options = {
    method: 'HEAD',
    host: uri.host,
    protocol: uri.protocol,
    port: uri.port,
    path: uri.path,
    timeout: 5 * 1000,
  };

  const req = request(options, (res) => {
    const { statusCode } = res;

    if (statusCode >= 200 && statusCode < 300) {
      resolve(target);
    } else {
      reject(new Error(`Url ${target} not found.`));
    }
  });

  req.on('error', reject);

  req.end();
});

它可以这样使用:

const urlExists = require('./url-exists')

urlExists('https://www.google.com')
  .then(() => {
    console.log('Google exists!');
  })
  .catch(() => {
    console.error('Invalid url :(');
  });

2
如果您正在使用 axios,您可以这样获取头部信息:
const checkUrl = async (url) => {
  try {
    await axios.head(fullUrl);
    return true;
  } catch (error) {
    if (error.response.status >= 400) {
      return false;
    }
  }
}

您可能希望自定义状态码范围以满足您的需求,例如,401(未授权)仍然可能意味着存在一个URL,但您无权访问。请参考状态码文档。

2
我看到你的代码已经在使用request库,所以只需要:
const request = require('request');

request.head('http://...', (error, res) => {
  const exists = !error && res.statusCode === 200;
});

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接