如何使用 nodejs 提供图像 [英] How to serve an image using nodejs

查看:25
本文介绍了如何使用 nodejs 提供图像的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I have a logo that is residing at the public/images/logo.gif. Here is my nodejs code.

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

它可以工作,但是当我请求 localhost:8080/logo.gif 时,我显然没有得到徽标.

It works but when I request for localhost:8080/logo.gif then I obviously don't get the logo.

我需要做哪些更改才能提供图片.

What changes I need to do to serve an image.

推荐答案

2016 更新

使用 Express 和不使用 Express 的示例实际有效

这个问题已经超过 5 年了,但每个答案都有一些问题.

向下滚动查看示例以提供图像:

Scroll down for examples to serve an image with:

  1. express.static
  2. express
  3. 连接
  4. http
  5. net

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

All of the examples are also on GitHub: https://github.com/rsp/node-static-http-servers

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

Test results are available on Travis: https://travis-ci.org/rsp/node-static-http-servers

自从提出这个问题超过 5 年之后,只有 一个正确答案generalhenry 但即使该答案的代码没有问题,但接收似乎存在一些问题.有人评论说,它除了如何依靠别人来完成工作之外,没有解释太多,而且有多少人对此评论进行了投票,这一事实清楚地表明很多事情需要澄清.

After over 5 years since this question was asked there is only one correct answer by generalhenry but even though that answer has no problems with the code, it seems to have some problems with reception. It was commented that it "doesn't explain much other than how to rely on someone else to get the job done" and the fact how many people have voted this comment up clearly shows that a lot of things need clarification.

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

First of all, a good answer to "How to serve images using Node.js" is not implementing a static file server from scratch and doing it badly. A good answer is using a module like Express that does the job correctly.

回答评论说使用 Express 除了如何依靠别人来完成工作之外没有解释太多" 应该注意的是,使用 http 模块已经依靠其他人来完成工作.如果有人不想依赖任何人来完成工作,那么至少应该使用原始 TCP 套接字来代替 - 我在下面的示例之一中这样做了.

Answering comments that say that using Express "doesn't explain much other than how to rely on someone else to get the job done" it should be noted, that using the http module already relies on someone else to get the job done. If someone doesn't want to rely on anyone to get the job done then at least raw TCP sockets should be used instead - which I do in one of my examples below.

一个更严重的问题是这里所有使用 http 模块的答案都坏了.它们引入了竞争条件不安全的路径解析,将导致路径遍历漏洞阻塞 I/O完全无法处理任何并发请求以及其他一些微妙的问题——它们作为问题所问的示例完全被破坏了,但它们已经使用了 http 提供的抽象 模块而不是使用 TCP 套接字,因此他们甚至不会像他们声称的那样从头开始做所有事情.

A more serious problem is that all of the answers here that use the http module are broken. They introduce race conditions, insecure path resolution that will lead to path traversal vulnerability, blocking I/O that will completely fail to serve any concurrent requests at all and other subtle problems - they are completely broken as examples of what the question asks about, and yet they already use the abstraction that is provided by the http module instead of using TCP sockets so they don't even do everything from scratch as they claim.

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

If the question was "How to implement static file server from scratch, as a learning exercise" then by all means answers how to do that should be posted - but even then we should expect them to at least be correct. Also, it is not unreasonable to assume that someone who wants to serve an image might want to serve more images in the future so one could argue that writing a specific custom static file server that can serve only one single file with hard-coded path is somewhat shortsighted. It seems hard to imagine that anyone who searches for an answer on how to serve an image would be content with a solution that serves just a single image instead of a general solution to serve any image.

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

In short, the question is how to serve an image and an answer to that is to use an appropriate module to do that in a secure, preformant and reliable way that is readable, maintainable and future-proof while using the best practice of professional Node development. But I agree that a great addition to such an answer would be showing a way to implement the same functionality manually but sadly every attempt to do that has failed so far. And that is why I wrote some new examples.

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

After this short introduction, here are my five examples doing the job on 5 different levels of abstraction.

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

Every example serves files from the public directory and supports the minumum functionality of:

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

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

I tested every version on Node versions 4, 5, 6 and 7.

此版本使用 express.staticexpress 模块的内置中间件.

This version uses the express.static built-in middleware of the express module.

这个例子具有最多的功能和最少的代码.

This example has the most functionality and the least amount of code.

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.static 中间件.提供静态文件是作为使用流的单个路由处理程序实现的.

express

This version uses the express module but without the express.static middleware. Serving static files is implemented as a single route handler using streams.

此示例具有简单的路径遍历对策,并支持有限的一组最常见的 MIME 类型.

This example has simple path traversal countermeasures and supports a limited set of most common MIME 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 模块,这是一个抽象级别低于express.

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

This example has similar functionality to the express version but using slightly lower-lever APIs.

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 模块,它是Node 中 HTTP 的最低级 API.

http

This version uses the http module which is the lowest-level API for HTTP in Node.

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

This example has similar functionality to the connect version but using even more lower-level APIs.

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 模块,它是Node 中 TCP 套接字的最低级 API.

net

This version uses the net module which is the lowest-level API for TCP sockets in Node.

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

This example has some of the functionality of the http version but the minimal and incomplete HTTP protocol has been implemented from scratch. Since it doesn't support chunked encoding it loads the files into memory before serving them to know the size before sending a response because statting the files and then loading would introduce a race condition.

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(/

?

?/)) {
            var line = input.split(/
/)[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
');
                con.write('Content-Type: text/plain
');
                con.write('Content-Length: '+body.length+'

');
                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
');
                con.write('Content-Type: text/plain
');
                con.write('Content-Length: '+body.length+'

');
                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
');
                    con.write('Content-Type: text/plain
');
                    con.write('Content-Length: '+body.length+'

');
                    con.write(body);
                    con.destroy();
                } else {
                    con.write('HTTP/1.1 200 OK
');
                    con.write('Content-Type: '+type+'
');
                    con.write('Content-Length: '+data.byteLength+'

');
                    con.write(data);
                    con.destroy();
                }
            });
        }
    });
});

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

下载示例

我在 GitHub 上发布了所有示例,并附有更多说明.

Download examples

I posted all of the examples on GitHub with more explanation.

express.staticexpressconnecthttpnet 的示例>:

Examples with express.static, express, connect, http and net:

仅使用 express.static 的其他项目:

Other project using only express.static:

测试结果可在 Travis 上获得:

Test results are available on Travis:

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

Everything is tested on Node versions 4, 5, 6, and 7.

其他相关答案:

这篇关于如何使用 nodejs 提供图像的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆