如何使用 Node.js 提供图像

2024-01-24

我有一个徽标位于public/images/logo.gif。这是我的nodejs代码。

http.createServer(function(req, res){
  res.writeHead(200, {'Content-Type': 'text/plain' });
  res.end('Hello World \n');
}).listen(8080, '127.0.0.1');

它有效,但是当我请求时localhost:8080/logo.gif那么我显然没有得到标志。

我需要做哪些改变才能提供图像。


2016年更新

使用 Express 和不使用 Express 的示例实际上工作

这个问题已有 5 年多了,但是每个答案都有一些问题.

TL;DR

向下滚动查看使用以下方式提供图像的示例:

  1. express.static
  2. express
  3. connect
  4. http
  5. net

所有示例也位于 GitHub 上:https://github.com/rsp/node-static-http-servers https://github.com/rsp/node-static-http-servers

测试结果可在 Travis 上获取:https://travis-ci.org/rsp/node-static-http-servers https://travis-ci.org/rsp/node-static-http-servers

介绍

自从这个问题被提出五年多以来,只有一个正确答案 https://stackoverflow.com/a/5823807/613198 by 亨利将军但即使这个答案与代码没有问题,它似乎有一些问题接待。有评论称它“除了如何依靠别人来完成工作之外,没有解释太多”事实上有多少人对此评论投了赞成票,这清楚地表明很多事情需要澄清。

首先,“如何使用 Node.js 提供图像”的一个很好的答案不是实现从头开始静态文件服务器并且做得很糟糕。一个好的答案是使用模块喜欢表达正确地完成工作.

回答说使用 Express 的评论“除了如何依靠别人来完成工作之外,没有解释太多”应该指出的是,使用http module already依靠别人来完成工作。如果有人不想依靠任何人来完成工作那么at least应该使用原始 TCP 套接字 - 我在下面的示例之一中就是这样做的。

一个更严重的问题是这里所有的答案都使用http模块是broken。他们介绍竞争条件, 不安全的路径解析这将导致路径遍历漏洞, 阻塞 I/O那将完全无法满足任何并发请求以及其他微妙的问题 - 它们作为问题所询问内容的示例完全被打破,但它们已经使用了由http模块而不是使用 TCP 套接字,因此他们甚至不像他们声称的那样从头开始做所有事情。

如果问题是“如何从头开始实现静态文件服务器,作为学习练习”,那么无论如何应该发布如何做到这一点的答案 - 但即便如此,我们也应该期望它们至少是correct。此外,假设想要提供图像的人可能希望在将来提供更多图像,这并不是没有道理的,因此有人可能会说,编写一个只能为具有硬编码路径的单个文件提供服务的特定自定义静态文件服务器是有点短视。似乎很难想象任何搜索如何提供图像的答案的人都会满足于仅提供单个图像的解决方案,而不是提供任何图像的通用解决方案。

简而言之,问题是如何提供图像,答案是使用适当的模块在安全、高性能且可靠的方式那是可读、可维护且面向未来在使用时最佳实践专业的 Node 开发。但我同意,对这样的答案的一个很好的补充是展示一种手动实现相同功能的方法,但遗憾的是到目前为止,每一次尝试都失败了。这就是我写一些新例子的原因。

在简短的介绍之后,下面是我在 5 个不同的抽象级别上完成这项工作的五个示例。

最低功能

每个示例都提供来自public目录并支持以下最低功能:

  • 最常见文件的 MIME 类型
  • 提供 HTML、JS、CSS、纯文本和图像
  • serves index.html作为默认目录索引
  • 响应丢失文件的错误代码
  • 无路径遍历漏洞
  • 读取文件时没有竞争条件

我在 Node 版本 4、5、6 和 7 上测试了每个版本。

express.static

该版本使用express.static https://expressjs.com/en/starter/static-files.html的内置中间件express https://expressjs.com/ module.

该示例功能最多,代码量最少。

var path = require('path');
var express = require('express');
var app = express();

var dir = path.join(__dirname, 'public');

app.use(express.static(dir));

app.listen(3000, function () {
    console.log('Listening on http://localhost:3000/');
});

express

该版本使用express https://expressjs.com/模块但没有express.static中间件。提供静态文件是使用流作为单个路由处理程序实现的。

该示例具有简单的路径遍历对策,并支持有限的大多数路径遍历对策。common MIME types https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types/Common_types.

var path = require('path');
var express = require('express');
var app = express();
var fs = require('fs');

var dir = path.join(__dirname, 'public');

var mime = {
    html: 'text/html',
    txt: 'text/plain',
    css: 'text/css',
    gif: 'image/gif',
    jpg: 'image/jpeg',
    png: 'image/png',
    svg: 'image/svg+xml',
    js: 'application/javascript'
};

app.get('*', function (req, res) {
    var file = path.join(dir, req.path.replace(/\/$/, '/index.html'));
    if (file.indexOf(dir + path.sep) !== 0) {
        return res.status(403).end('Forbidden');
    }
    var type = mime[path.extname(file).slice(1)] || 'text/plain';
    var s = fs.createReadStream(file);
    s.on('open', function () {
        res.set('Content-Type', type);
        s.pipe(res);
    });
    s.on('error', function () {
        res.set('Content-Type', 'text/plain');
        res.status(404).end('Not found');
    });
});

app.listen(3000, function () {
    console.log('Listening on http://localhost:3000/');
});

connect

该版本使用connect http://senchalabs.github.com/connect模块是低于一个抽象级别express.

此示例具有与以下类似的功能express版本,但使用级别稍低的 API。

var path = require('path');
var connect = require('connect');
var app = connect();
var fs = require('fs');

var dir = path.join(__dirname, 'public');

var mime = {
    html: 'text/html',
    txt: 'text/plain',
    css: 'text/css',
    gif: 'image/gif',
    jpg: 'image/jpeg',
    png: 'image/png',
    svg: 'image/svg+xml',
    js: 'application/javascript'
};

app.use(function (req, res) {
    var reqpath = req.url.toString().split('?')[0];
    if (req.method !== 'GET') {
        res.statusCode = 501;
        res.setHeader('Content-Type', 'text/plain');
        return res.end('Method not implemented');
    }
    var file = path.join(dir, reqpath.replace(/\/$/, '/index.html'));
    if (file.indexOf(dir + path.sep) !== 0) {
        res.statusCode = 403;
        res.setHeader('Content-Type', 'text/plain');
        return res.end('Forbidden');
    }
    var type = mime[path.extname(file).slice(1)] || 'text/plain';
    var s = fs.createReadStream(file);
    s.on('open', function () {
        res.setHeader('Content-Type', type);
        s.pipe(res);
    });
    s.on('error', function () {
        res.setHeader('Content-Type', 'text/plain');
        res.statusCode = 404;
        res.end('Not found');
    });
});

app.listen(3000, function () {
    console.log('Listening on http://localhost:3000/');
});

http

该版本使用http https://nodejs.org/api/http.html#http_http模块是 Node.js 中 HTTP 的最低级别 API。

此示例具有与以下类似的功能connect版本,但使用更低级别的 API。

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

var dir = path.join(__dirname, 'public');

var mime = {
    html: 'text/html',
    txt: 'text/plain',
    css: 'text/css',
    gif: 'image/gif',
    jpg: 'image/jpeg',
    png: 'image/png',
    svg: 'image/svg+xml',
    js: 'application/javascript'
};

var server = http.createServer(function (req, res) {
    var reqpath = req.url.toString().split('?')[0];
    if (req.method !== 'GET') {
        res.statusCode = 501;
        res.setHeader('Content-Type', 'text/plain');
        return res.end('Method not implemented');
    }
    var file = path.join(dir, reqpath.replace(/\/$/, '/index.html'));
    if (file.indexOf(dir + path.sep) !== 0) {
        res.statusCode = 403;
        res.setHeader('Content-Type', 'text/plain');
        return res.end('Forbidden');
    }
    var type = mime[path.extname(file).slice(1)] || 'text/plain';
    var s = fs.createReadStream(file);
    s.on('open', function () {
        res.setHeader('Content-Type', type);
        s.pipe(res);
    });
    s.on('error', function () {
        res.setHeader('Content-Type', 'text/plain');
        res.statusCode = 404;
        res.end('Not found');
    });
});

server.listen(3000, function () {
    console.log('Listening on http://localhost:3000/');
});

net

该版本使用net https://nodejs.org/api/net.html#net_net模块是 Node.js 中 TCP 套接字的最低级别 API。

这个例子有一些功能http版本,但最小且不完整的 HTTP 协议已从头开始实现。由于它不支持分块编码,因此在发送响应之前将文件加载到内存中以了解文件的大小,因为先统计文件然后加载会引入竞争条件。

var path = require('path');
var net = require('net');
var fs = require('fs');

var dir = path.join(__dirname, 'public');

var mime = {
    html: 'text/html',
    txt: 'text/plain',
    css: 'text/css',
    gif: 'image/gif',
    jpg: 'image/jpeg',
    png: 'image/png',
    svg: 'image/svg+xml',
    js: 'application/javascript'
};

var server = net.createServer(function (con) {
    var input = '';
    con.on('data', function (data) {
        input += data;
        if (input.match(/\n\r?\n\r?/)) {
            var line = input.split(/\n/)[0].split(' ');
            var method = line[0], url = line[1], pro = line[2];
            var reqpath = url.toString().split('?')[0];
            if (method !== 'GET') {
                var body = 'Method not implemented';
                con.write('HTTP/1.1 501 Not Implemented\n');
                con.write('Content-Type: text/plain\n');
                con.write('Content-Length: '+body.length+'\n\n');
                con.write(body);
                con.destroy();
                return;
            }
            var file = path.join(dir, reqpath.replace(/\/$/, '/index.html'));
            if (file.indexOf(dir + path.sep) !== 0) {
                var body = 'Forbidden';
                con.write('HTTP/1.1 403 Forbidden\n');
                con.write('Content-Type: text/plain\n');
                con.write('Content-Length: '+body.length+'\n\n');
                con.write(body);
                con.destroy();
                return;
            }
            var type = mime[path.extname(file).slice(1)] || 'text/plain';
            var s = fs.readFile(file, function (err, data) {
                if (err) {
                    var body = 'Not Found';
                    con.write('HTTP/1.1 404 Not Found\n');
                    con.write('Content-Type: text/plain\n');
                    con.write('Content-Length: '+body.length+'\n\n');
                    con.write(body);
                    con.destroy();
                } else {
                    con.write('HTTP/1.1 200 OK\n');
                    con.write('Content-Type: '+type+'\n');
                    con.write('Content-Length: '+data.byteLength+'\n\n');
                    con.write(data);
                    con.destroy();
                }
            });
        }
    });
});

server.listen(3000, function () {
    console.log('Listening on http://localhost:3000/');
});

下载示例

我在 GitHub 上发布了所有示例并附有更多解释。

示例与express.static, express, connect, http and net:

  • https://github.com/rsp/node-static-http-servers https://github.com/rsp/node-static-http-servers

其他项目仅使用express.static:

  • https://github.com/rsp/node-express-static-example https://github.com/rsp/node-express-static-example

Tests

测试结果可在 Travis 上获取:

  • https://travis-ci.org/rsp/node-static-http-servers https://travis-ci.org/rsp/node-static-http-servers

一切都在 Node 版本 4、5、6 和 7 上进行了测试。

See also

其他相关回答:

  • 重定向 Javascript 时无法从同一目录加载资源 https://stackoverflow.com/questions/38441863/failed-to-load-resource-from-same-directory-when-redirecting-javascript/38442747#38442747
  • onload js 调用不适用于节点 https://stackoverflow.com/questions/38587286/onload-js-call-not-working-with-node/38587729#38587729
  • 通过快递将整个文件夹内容发送给客户端 https://stackoverflow.com/questions/40509666/sending-whole-folder-content-to-client-with-express/40510339#40510339
  • 在服务器 JS 上加载部分失败 https://stackoverflow.com/questions/40722476/loading-partials-fails-on-the-server-js/40722594#40722594
  • Node JS 不提供静态图像 https://stackoverflow.com/questions/40837359/node-js-not-serving-the-static-image/40839534#40839534
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何使用 Node.js 提供图像 的相关文章

  • Nodejs 中的子域

    如何处理nodejs的子域请求 例如以下代码回显test在控制台中的任何请求http localhost 9876 任何内容 http localhost 9876 5Banything 5D var http require http h
  • Nodejs 调试生产中的错误

    我有一个在生产环境中运行的 Nodejs 脚本 我不太可能 千分之一 遇到这样的错误 TypeError value is out of bounds at checkInt buffer js 1009 11 at Buffer writ
  • Meteor.setTimeout 和 Meteor.methods 之间的并发

    在我的 Meteor 应用程序中实现回合制多人游戏服务器 客户端通过发布 订阅接收游戏状态 并且可以调用 Meteor 方法sendTurn将回合数据发送到服务器 他们无法直接更新游戏状态集合 var endRound function g
  • 呃!尝试将包发布到 npm 时出现 403

    我正在尝试将包发布到 npm 您可以在此处查看存储库 https github com biowaffeln mdx state https github com biowaffeln mdx state 我登录到 npmnpm login
  • 使用socket.io进行用户身份验证

    我已经红色了这个教程 http howtonode org socket io auth http howtonode org socket io auth 它展示了如何使用express和socket io对用户进行身份验证 但是有没有一
  • Jwt 签名和前端登录身份验证

    我有这个特殊的 jwt sign 函数 Backend const token jwt sign id user id process env TOKEN SECRET expiresIn 1m res header auth token
  • 如何使用 sinon/mocha 模拟 npm 模块

    我正在尝试测试调用该模块的函数cors 我想测试一下cors会被称为 为此 我必须存根 模拟它 这是函数 cors js const cors require cors const setCors gt cors origin http l
  • npmjs.org - 找不到自述文件

    我是 npm 包的主要作者scramjet 一个月以来 我遇到了关于可视性的问题README md在 npmjs 中 The npm 中的超燃冲压发动机包 https www npmjs com package scramjet shows
  • 如何在Electron WebView中连接到代理?

    因为我可以通过连接到免费代理服务器 或付费 目前用作电子 JS 解决方案作为桌面应用程序 代理列表服务器示例 http proxylist hidemyass com http proxylist hidemyass com 您可以使用 s
  • Node JS - 如何检测用户语言

    我正在尝试在纯 Node js 中检查用户语言 而不使用 npm 的任何扩展 我使用了进程和全局命令 有很多规范但我找不到系统语言 这是获取默认区域设置的独立于操作系统 节点 浏览器的方法 let locale Intl DateTimeF
  • Node.js 重写 toString

    我试图覆盖我的对象的默认 toString 方法 这是代码和问题 function test this code 0 later on I will set these this name test prototype toString f
  • 节点无法抓取某些页面

    我不知道这是否与冷融合页面有关 但我无法刮掉这些 cfm pages 在目录中的命令行中request run node gt var request require request node gt var url http linguis
  • 如何使用Create React App安装React

    嗨 我对反应真的很陌生 我不知道如何实际安装它 也不知道我需要做什么才能在其中编写代码 我下载了node js并且安装了v12 18 3以及NPM 6 14 6 但是每次我尝试在许多网站上提到的create react app安装方法中输入
  • Node.js 和 Passport 对象没有 validPassword 方法

    我正在使用 Node js Express Passport 创建一个简单的身份验证 本地 到目前为止我所达到的效果是 当输入错误的用户名或密码时 用户将被重定向到错误页面 但是当用户输入正确的用户名和密码时 我收到此错误 node mod
  • 如何使用remark将markdown解析为json

    The 备注站点 https remark js org 有一个 AST 浏览器的链接 用于输出备注 https astexplorer net gist 0a92bbf654aca4fdfb3f139254cf0bad ffe102014
  • 如何配置 Google 计算引擎以对 Nodejs 服务器使用 HTTPS?

    我想使用 https SSL 在 google 计算引擎中运行 nodejs 和 socket io 服务器 我安装了自签名证书https cloud google com compute docs load balancing http
  • 使用 Sequelize 实现单表继承

    有没有办法使用sequelize来创建单表继承 我希望有一个用于购买和 PartialPurchase 模型的 STI 其中我将有一个类型字段 该字段为 Purchase 或 PartialPurchase 以及类 Purchasing 和
  • 通过 node-http-proxy 保留基于 cookie 的会话

    我有一个简单的基于 Express 的 Node js Web 服务器 用于开发 JavaScript 应用程序 我将服务器设置为使用 node http proxy 来代理应用程序向在不同域和端口上运行的 Jetty 服务器发出的 API
  • 未捕获的错误:找不到模块“jquery”

    我在用Electron https github com atom electron制作桌面应用程序 在我的应用程序中 我正在加载一个外部站点 Atom 应用程序之外 可以说http mydummysite index html http
  • Nodejs mysql 获取正确的时间戳格式

    我在用着mysqljs https github com mysqljs mysql得到结果后sql我变得不同TimeStamp格式如下 created at Sat Jul 16 2016 23 52 54 GMT 0430 IRDT 但

随机推荐