Answers:
这个问题已经5年了,但是每个答案都有一些问题。
向下滚动示例以使用以下图片投放图片:
express.staticexpressconnecthttpnet所有示例也都在GitHub上:https : //github.com/rsp/node-static-http-servers
可在Travis上获得测试结果:https : //travis-ci.org/rsp/node-static-http-servers
自从问了这个问题以来的5年多之后,通俗的回答只有一个正确的答案,但是即使该答案在代码上没有问题,也似乎在接收方面存在一些问题。有人评论说,“除了如何依靠别人来完成工作外,它没有其他解释”,而且有多少人投票赞成这一评论,这一事实清楚地表明,很多事情需要澄清。
首先,“如何使用Node.js提供图像”的一个很好的答案不是从头开始实现静态文件服务器,而是做得不好。一个很好的答案是使用 Express这样的模块来正确地完成工作。
回答说使用Express “除了如何依靠别人来完成工作外,没有什么其他解释”,应该指出,使用http模块已经依靠别人来完成工作。如果某人不想依靠任何人来完成工作,那么至少应该使用原始的TCP套接字代替-我在下面的示例之一中这样做。
一个更严重的问题是,此处使用该http模块的所有答案都被破坏了。它们引入了竞争条件,不安全的路径解析(将导致路径穿越漏洞),阻止将完全无法满足所有并发请求以及其他细微问题的I / O-它们被完全破坏为问题的示例,并且但是他们已经使用了http模块提供的抽象,而不是使用TCP套接字,因此他们甚至没有像声称的那样从头开始做任何事情。
如果问题是“如何从头开始实现静态文件服务器,作为一项学习练习”,则应通过各种方式回答应发布的内容-但即使如此,我们也应该期望它们至少是正确的。同样,并非毫无道理地假设有人想要提供图像,将来可能会提供更多图像,因此可以认为编写特定的自定义静态文件服务器只能提供一个带有硬编码路径的单个文件是合理的。有点短视。似乎很难想象,谁能找到有关如何提供图像的答案,那么他们将对仅提供单个图像的解决方案而不是提供任何图像的通用解决方案感到满意。
简而言之,问题是如何提供图像,对此的答案是使用适当的模块以安全,高效,可靠的方式做到这一点,该方式具有可读性,可维护性和面向未来,同时使用专业Node 的最佳实践发展。但是我同意,对这种答案的一个很好的补充将是显示一种手动实现相同功能的方法,但遗憾的是,到目前为止,每次尝试都失败了。这就是为什么我写了一些新的例子。
简短介绍之后,这是我的五个示例,分别在5个不同的抽象级别上进行工作。
每个示例都提供public目录中的文件,并支持以下最低功能:
index.html默认目录索引我在Node版本4、5、6和7上测试了每个版本。
express.static此版本使用模块的express.static内置中间件express。
此示例具有最多的功能和最少的代码量。
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模块,但没有express.static中间件。服务静态文件是使用流作为单个路由处理程序实现的。
此示例具有简单的路径遍历对策,并支持一组有限的大多数常见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模块比少一个抽象级别express。
此示例具有与express版本相似的功能,但使用的杠杆级别稍低。
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。
该示例与该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模块是Node中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和net:
仅使用其他项目express.static:
测试结果可在Travis上获得:
一切都在节点版本4、5、6和7上进行了测试。
其他相关答案:
express.static我们可以通过调用url来获取图像http://ip:port/path_after_the_static_folder,我们无需提及静态文件夹本身即可提供图像。尽管app.use('/static', express.static(imagePath))为了方便起见,我们可以添加以下内容作为标准文档:expressjs.com/en/starter/static-files.html
我同意其他张贴者的观点,最终,您应该使用诸如Express ..的框架。但是首先,您还应该了解如何在没有库的情况下进行类似的基本操作,以真正了解库为您抽象的内容。步骤是
代码看起来像这样(未经测试)
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');
res.end(img);应为res.end(img, 'binary');。干得好!
您应该使用快速框架。
npm install express
然后
var express = require('express');
var app = express();
app.use(express.static(__dirname + '/public'));
app.listen(8080);
然后网址localhost:8080 / images / logo.gif应该可以使用。
为时已晚,但可以帮助某人,我正在使用node version v7.9.0和express version 4.15.0
如果您的目录结构是这样的:
your-project
uploads
package.json
server.js
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);
要求的Vanilla节点版本:
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');
fs.exists(竞赛条件),这是在管道传输时发现错误的更好习惯。
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');
我喜欢将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();
}
这种方法对我有用,它不是动态的,但直截了当:
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 );
//This method involves directly integrating HTML Code in the res.write
//first time posting to stack ...pls be kind
const express = require('express');
const app = express();
const https = require('https');
app.get("/",function(res,res){
res.write("<img src="+image url / src +">");
res.send();
});
app.listen(3000, function(req, res) {
console.log("the server is onnnn");
});