如何使用 nodejs 提供图像
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/5823722/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to serve an image using nodejs
提问by Nick Vanderbilt
I have a logo that is residing at the public/images/logo.gif . Here is my nodejs code.
我有一个位于 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');
It works but when I request for localhost:8080/logo.gif then I obviously I don't get the logo.
它有效,但是当我请求 localhost:8080/logo.gif 时,我显然没有得到徽标。
What changes I need to do to serve an image.
我需要做哪些更改才能提供图像。
回答by rsp
2016 Update
2016年更新
Examples with Express and without Express that actually work
使用 Express 和不使用 Express 的实际工作示例
This question is over 5 years old but every answer has some problems.
这个问题已经超过 5 年了,但每个答案都有一些问题。
TL;DR
TL; 博士
Scroll down for examples to serve an image with:
向下滚动示例以提供图像:
express.staticexpressconnecthttpnet
express.staticexpressconnecthttpnet
All of the examples are also on GitHub: https://github.com/rsp/node-static-http-servers
所有示例也在 GitHub 上:https: //github.com/rsp/node-static-http-servers
Test results are available on Travis: https://travis-ci.org/rsp/node-static-http-servers
测试结果可在 Travis 上获得:https: //travis-ci.org/rsp/node-static-http-servers
Introduction
介绍
After over 5 years since this question was asked there is only one correct answerby generalhenrybut 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.
自从提出这个问题超过 5 年之后,generalhenry只有一个正确答案,但即使该答案与代码没有问题,但它似乎在接收方面存在一些问题。有人评论说,它“除了如何依靠别人来完成工作之外,并没有解释太多”,而且有多少人对这条评论投了赞成票,这一事实清楚地表明,很多事情需要澄清。
First of all, a good answer to "How to serve images using Node.js" is not implementing a static file server from scratchand doing it badly. A good answer is using a modulelike Express that does the job correctly.
首先,“如何使用 Node.js 提供图像服务”的一个很好的答案不是从头开始实现静态文件服务器并且做得很糟糕。一个好的答案是使用像 Express 这样的模块可以正确地完成工作。
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 httpmodule alreadyrelies on someone else to get the job done. If someone doesn't want to rely on anyone to get the job done then at leastraw TCP sockets should be used instead - which I do in one of my examples below.
回答那些说使用 Express “除了如何依靠别人来完成工作之外没有解释太多”的评论时,应该注意的是,使用该http模块已经依赖于其他人来完成工作。如果有人不想依赖任何人来完成工作,那么至少应该使用原始 TCP 套接字 - 我在下面的示例之一中这样做了。
A more serious problem is that all of the answers here that use the httpmodule are broken. They introduce race conditions, insecure path resolutionthat will lead to path traversal vulnerability, blocking I/Othat will completely fail to serve any concurrent requestsat 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 httpmodule instead of using TCP sockets so they don't even do everything from scratch as they claim.
一个更严重的问题是这里所有使用该http模块的答案都被破坏了。它们引入了竞争条件、不安全的路径解析会导致路径遍历漏洞、阻塞 I/O将完全无法为任何并发请求提供服务以及其他微妙的问题——作为问题所问的例子,它们完全被破坏了,以及然而他们已经使用了http模块提供的抽象而不是使用 TCP 套接字,所以他们甚至没有像他们声称的那样从头开始做所有事情。
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.
如果问题是“如何从头开始实现静态文件服务器,作为一项学习练习”,那么无论如何都应该发布如何做到这一点的答案 - 但即便如此,我们也应该期望它们至少是正确的。此外,假设想要提供图像的人可能希望在未来提供更多图像并不是没有道理的,因此有人可能会争辩说,编写一个特定的自定义静态文件服务器,它只能为一个具有硬编码路径的文件提供服务有点短视。似乎很难想象,任何搜索如何提供图像的答案的人都会满足于只提供单个图像的解决方案,而不是提供任何图像的通用解决方案。
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 waythat is readable, maintainable and future-proofwhile using the best practiceof 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.
总之,问题是如何服务的图像和答案是使用适当的模块,这样做在一个安全的,preformant和可靠的方法是可读的,可维护的和面向未来的同时采用了最佳实践的专业节点发展。但我同意,对这样一个答案的一个很好的补充是展示一种手动实现相同功能的方法,但遗憾的是,到目前为止,每一次尝试都失败了。这就是为什么我写了一些新的例子。
After this short introduction, here are my five examples doing the job on 5 different levels of abstraction.
在这个简短的介绍之后,这里是我在 5 个不同抽象级别上完成工作的五个示例。
Minimum functionality
最低限度的功能
Every example serves files from the publicdirectory and supports the minumum functionality of:
每个示例都提供目录中的public文件并支持以下最低功能:
- MIME types for most common files
- serves HTML, JS, CSS, plain text and images
- serves
index.htmlas a default directory index - responds with error codes for missing files
- no path traversal vulnerabilities
- no race conditions while reading files
- 最常见文件的 MIME 类型
- 提供 HTML、JS、CSS、纯文本和图像
- 服务
index.html作为默认目录索引 - 响应丢失文件的错误代码
- 无路径遍历漏洞
- 读取文件时没有竞争条件
I tested every version on Node versions 4, 5, 6 and 7.
我在 Node 版本 4、5、6 和 7 上测试了每个版本。
express.static
express.static
This version uses the express.staticbuilt-in middleware of the expressmodule.
此版本使用模块的express.static内置中间件express。
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
This version uses the expressmodule but without the express.staticmiddleware. Serving static files is implemented as a single route handler using streams.
此版本使用express模块但没有express.static中间件。提供静态文件是作为使用流的单个路由处理程序实现的。
This example has simple path traversal countermeasures and supports a limited set of most common MIME types.
此示例具有简单的路径遍历对策并支持一组有限的最常见 MIME 类型。
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
This version uses the connectmodule which is a one level of abstraction lower than express.
此版本使用的connect模块比express.
This example has similar functionality to the expressversion but using slightly lower-lever APIs.
此示例具有与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
This version uses the httpmodule which is the lowest-level API for HTTP in Node.
该版本使用httpNode.js 中最底层的 HTTP API 模块。
This example has similar functionality to the connectversion but using even more lower-level APIs.
此示例具有与该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
This version uses the netmodule which is the lowest-level API for TCP sockets in Node.
此版本使用netNode.js 中 TCP 套接字的最低级 API 模块。
This example has some of the functionality of the httpversion 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.
此示例具有该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/');
});
Download examples
下载示例
I posted all of the examples on GitHub with more explanation.
我在 GitHub 上发布了所有示例,并附有更多说明。
Examples with express.static, express, connect, httpand net:
例子有express.static,express,connect,http和net:
Other project using only express.static:
其他项目仅使用express.static:
Tests
测试
Test results are available on Travis:
测试结果可在 Travis 上获得:
Everything is tested on Node versions 4, 5, 6, and 7.
一切都在 Node 版本 4、5、6 和 7 上进行了测试。
See also
也可以看看
Other related answers:
其他相关回答:
回答by noli
I agree with the other posters that eventually, you should use a framework, such as Express.. but first you should also understand how to do something fundamental like this without a library, to really understand what the library abstracts away for you.. The steps are
我同意其他海报,最终,你应该使用一个框架,比如 Express.. 但首先你还应该了解如何在没有图书馆的情况下做这样的基本事情,真正了解图书馆为你抽象出来的东西。步骤是
- Parse the incoming HTTP request, to see which path the user is asking for
- Add a pathway in conditional statement for the server to respond to
- If the image is requested, read the image file from the disk.
- Serve the image content-type in a header
- Serve the image contents in the body
- 解析传入的 HTTP 请求,查看用户请求的路径
- 在条件语句中添加路径供服务器响应
- 如果请求图像,则从磁盘读取图像文件。
- 在标题中提供图像内容类型
- 在正文中提供图像内容
The code would look something like this (not tested)
代码看起来像这样(未测试)
fs = require('fs');
http = require('http');
url = require('url');
http.createServer(function(req, res){
var request = url.parse(req.url, true);
var action = request.pathname;
if (action == '/logo.gif') {
var img = fs.readFileSync('./logo.gif');
res.writeHead(200, {'Content-Type': 'image/gif' });
res.end(img, 'binary');
} else {
res.writeHead(200, {'Content-Type': 'text/plain' });
res.end('Hello World \n');
}
}).listen(8080, '127.0.0.1');
回答by generalhenry
You should use the expressframework.
您应该使用express框架。
npm install express
and then
进而
var express = require('express');
var app = express();
app.use(express.static(__dirname + '/public'));
app.listen(8080);
and then the URL localhost:8080/images/logo.gifshould work.
然后 URL localhost:8080/images/logo.gif应该可以工作。
回答by Muhammad Shahzad
It is too late but helps someone, I'm using node version v7.9.0and express version 4.15.0
为时已晚,但可以帮助某人,我正在使用node version v7.9.0和express version 4.15.0
if your directory structure is something like this:
如果你的目录结构是这样的:
your-project
uploads
package.json
server.js
server.js code:
server.js 代码:
var express = require('express');
var app = express();
app.use(express.static(__dirname + '/uploads'));// you can access image
//using this url: http://localhost:7000/abc.jpg
//make sure `abc.jpg` is present in `uploads` dir.
//Or you can change the directory for hiding real directory name:
`app.use('/images', express.static(__dirname+'/uploads/'));// you can access image using this url: http://localhost:7000/images/abc.jpg
app.listen(7000);
回答by generalhenry
Vanilla node version as requested:
根据要求的香草节点版本:
var http = require('http');
var url = require('url');
var path = require('path');
var fs = require('fs');
http.createServer(function(req, res) {
// parse url
var request = url.parse(req.url, true);
var action = request.pathname;
// disallow non get requests
if (req.method !== 'GET') {
res.writeHead(405, {'Content-Type': 'text/plain' });
res.end('405 Method Not Allowed');
return;
}
// routes
if (action === '/') {
res.writeHead(200, {'Content-Type': 'text/plain' });
res.end('Hello World \n');
return;
}
// static (note not safe, use a module for anything serious)
var filePath = path.join(__dirname, action).split('%20').join(' ');
fs.exists(filePath, function (exists) {
if (!exists) {
// 404 missing files
res.writeHead(404, {'Content-Type': 'text/plain' });
res.end('404 Not Found');
return;
}
// set the content type
var ext = path.extname(action);
var contentType = 'text/plain';
if (ext === '.gif') {
contentType = 'image/gif'
}
res.writeHead(200, {'Content-Type': contentType });
// stream the file
fs.createReadStream(filePath, 'utf-8').pipe(res);
});
}).listen(8080, '127.0.0.1');
回答by Mahadev Mirasdar
var http = require('http');
var fs = require('fs');
http.createServer(function(req, res) {
res.writeHead(200,{'content-type':'image/jpg'});
fs.createReadStream('./image/demo.jpg').pipe(res);
}).listen(3000);
console.log('server running at 3000');
回答by occasl
I like using Restifyfor REST services. In my case, I had created a REST service to serve up images and then if an image source returned 404/403, I wanted to return an alternative image. Here's what I came up with combining some of the stuff here:
我喜欢将Restify用于 REST 服务。就我而言,我创建了一个 REST 服务来提供图像,然后如果图像源返回 404/403,我想返回一个替代图像。这是我在这里结合一些东西的想法:
function processRequest(req, res, next, url) {
var httpOptions = {
hostname: host,
path: url,
port: port,
method: 'GET'
};
var reqGet = http.request(httpOptions, function (response) {
var statusCode = response.statusCode;
// Many images come back as 404/403 so check explicitly
if (statusCode === 404 || statusCode === 403) {
// Send default image if error
var file = 'img/user.png';
fs.stat(file, function (err, stat) {
var img = fs.readFileSync(file);
res.contentType = 'image/png';
res.contentLength = stat.size;
res.end(img, 'binary');
});
} else {
var idx = 0;
var len = parseInt(response.header("Content-Length"));
var body = new Buffer(len);
response.setEncoding('binary');
response.on('data', function (chunk) {
body.write(chunk, idx, "binary");
idx += chunk.length;
});
response.on('end', function () {
res.contentType = 'image/jpg';
res.send(body);
});
}
});
reqGet.on('error', function (e) {
// Send default image if error
var file = 'img/user.png';
fs.stat(file, function (err, stat) {
var img = fs.readFileSync(file);
res.contentType = 'image/png';
res.contentLength = stat.size;
res.end(img, 'binary');
});
});
reqGet.end();
return next();
}
回答by marko-36
This may be a bit off-topic, since you are asking about static file serving via Node.js specifically (where fs.createReadStream('./image/demo.jpg').pipe(res)is actually a good idea), but in production you may want to have your Node app handle tasks, that cannot be tackled otherwise, and off-load static serving to e.g Nginx.
这可能有点题外话,因为您专门询问通过 Node.js 提供静态文件(fs.createReadStream('./image/demo.jpg').pipe(res)实际上这是一个好主意),但在生产中您可能希望让您的 Node 应用程序处理任务,否则无法解决,并将静态服务卸载到例如 Nginx。
This means less coding inside your app, and better efficiency since reverse proxies are by design ideal for this.
这意味着在您的应用程序中更少的编码,以及更高的效率,因为反向代理是设计上的理想选择。
回答by Nik.H
Let me just add to the answers above, that optimizing images, and serving responsive images helps page loading times dramatically, since >90% of web traffic are images. You might want to pre-process images using JS / Node modulessuch as imageminand related plug-ins, ideally during the build process with Grunt or Gulp.
让我补充一下上面的答案,优化图像和提供响应式图像有助于显着缩短页面加载时间,因为 > 90% 的网络流量是图像。您可能希望使用 JS/Node 模块(例如imagemin和相关插件)预处理图像,最好在使用 Grunt 或Gulp的构建过程中进行。
Optimizing imagesmeans processing finding an ideal image type, and selecting optimal compression to achieve a balance between image quality and file size.
优化图像意味着处理寻找理想的图像类型,并选择最佳压缩来实现图像质量和文件大小之间的平衡。
Serving responsive imagestranslates into creating several sizes and formats of each image automatically and using srcsetin your html allow you to serve optimal image set (that is, the ideal format and dimensions, thus optimal file size) for each single browser).
提供响应式图像转换为自动创建每个图像的多种尺寸和格式,并srcset在您的 html 中使用允许您为每个浏览器提供最佳图像集(即理想的格式和尺寸,从而优化文件大小)。
Image processing automationduring build process means incorporating it up once, and presenting optimized images further on, requiring minimum extra time.
构建过程中的图像处理自动化意味着将其合并一次,并进一步呈现优化的图像,所需的额外时间最少。
Some great read on responsive images, minificationin general, imageminnode module and using srcset.
回答by Nik.H
This method works for me, it's not dynamic but straight to the point:
这种方法对我有用,它不是动态的,而是直截了当的:
const fs = require('fs');
const express = require('express');
const app = express();
app.get( '/logo.gif', function( req, res ) {
fs.readFile( 'logo.gif', function( err, data ) {
if ( err ) {
console.log( err );
return;
}
res.write( data );
return res.end();
});
});
app.listen( 80 );

