如何使用nodejs提供图像

我有一个标志居住在公共/ 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'); 

它的工作原理,但是当我要求本地主机:8080 / logo.gif那么我显然我没有得到的标志。

我需要做什么改变来提供图像。

我同意其他海报,最终,你应该使用一个框架,如Express ..但首先,你也应该了解如何做一些基本的东西,这样没有图书馆,真正理解图书馆为你抽象。步骤是

  1. 解析传入的HTTP请求,查看用户请求的路径
  2. 在条件语句中添加一条路径供服务器响应
  3. 如果图像被请求,从磁盘读取图像文件。
  4. 在标题中提供图片内容类型
  5. 提供人体内的图像内容

代码看起来像这样(未测试)

 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'); 

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

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

介绍

这个问题问了5年以上,问题只有一个正确的答案 ,尽管答案没有问题,但在接受方面似乎有些问题。 有人评论说, 除了如何依靠别人来完成工作外,没有多少解释,而有多少人对这个评论投了赞成票,这清楚地表明,很多事情都需要澄清。

首先,“如何使用Node.js服务映像”的一个很好的答案是不从头开始实施一个静态文件服务器,并做得不好。 一个好的答案是使用像Express这样的模块正确地完成工作

回答评论说,使用Express 并不能说明除了如何依靠别人来完成工作外 ,应该指出的是,使用http模块已经依赖于别人来完成工作。 如果有人不想依靠任何人来完成工作,那么至少应该使用原始的TCP套接字,而不是我在下面的一个例子中做的。

更严重的问题是所有使用http模块的答案都被破坏了 。 它们会引入竞争条件 ,导致路径穿越漏洞的 不安全路径解决方案阻止完全无法提供任何并发请求的 I / O以及其他微妙的问题 – 它们被彻底打破,作为问题所要求的例子,但是他们已经使用了由http模块提供的抽象,而不是使用TCP套接字,所以他们甚至都没有从头开始做所有事情。

如果问题是“如何从头开始实施静态文件服务器,作为一个学习练习”,那么通过一切办法来回答如何做到这一点应该张贴 – 但即使如此,我们应该期望他们至少是正确的 。 另外,假设想要提供图像的人可能想在未来提供更多的图像,所以人们可能会争辩说,编写一个特定的自定义静态文件服务器,该服务器只能为硬编码的路径提供一个单独的文件有点短视。 似乎很难想象,任何搜索如何提供图像的答案的人都会满足于只提供单个图像的解决方案,而不是提供任何图像的通用解决方案。

简而言之,问题在于如何提供图像和答案,就是使用合适的模块以安全,优先和可靠的方式进行操作 ,这种方式在使用专业节点的最佳实践时是可读的,可维护的和面向未来的发展。 但是我同意,对这样一个答案的一个很好的补充将会展示一种手动实现相同的功能的方法,但是可悲的是到目前为止所有的尝试都失败了。 这就是为什么我写了一些新的例子。

在这个简短的介绍之后,这里是我的五个例子在5个不同的抽象层次上做这个工作。

最小的功能

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

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

我测试了节点版本4,5,6和7上的每个版本。

express.static

该版本使用express模块的express.static内置中间件。

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

 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

该版本使用低于express的抽象级别的connect模块。

这个例子具有与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

此版本使用Node中HTTP的最低级别的http模块。

这个例子具有与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

此版本使用Node中的TCP套接字的最低级别的net模块。

这个例子有一些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.staticexpressconnecthttpnet例子:

其他项目只使用express.static

测试

测试结果可以在Travis上找到:

一切都在节点版本4,5,6和7上进行测试。

也可以看看

其他相关答案:

  • 重定向Javascript时无法从同一目录加载资源
  • onload js调用不与节点一起工作
  • 用express发送整个文件夹内容给客户端
  • 在服务器JS上加载partials失败
  • 节点JS不提供静态图像

你应该使用快速框架。

npm install express

 var express = require('express'); var app = express(); app.use(express.static(__dirname + '/public')); app.listen(8080); 

然后url localhost:8080 / images / logo.gif应该可以工作

我喜欢使用Restify for 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(); } 

香草节点版本请求:

 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'); 

这太晚了,但帮助别人,我使用node version v7.9.0express 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:7000http://img.dovov.comabc.jpg app.listen(7000); 

您需要使用知道URL和静态文件的库。 我建议使用Express 。 它具有设置路由的功能,以及静态文件服务模块。