草庐IT

node.js - 如何使用 nodejs 提供图像

coder 2023-05-28 原文

我有一个位于 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; 博士

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

  • express.static
  • express
  • connect
  • http
  • net

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

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

    介绍

    自从提出这个问题超过 5 年后,只有 one correct answer来自 亨利将军 但即使该答案对代码没有问题, 似乎也有一些问题。前台 .有人评论说,它“除了如何依靠别人来完成工作之外没有解释太多”,而且有多少人投票支持这条评论的事实清楚地表明很多事情需要澄清。

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

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

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

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

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

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

    最低限度的功能

    每个示例都提供来自 public 的文件目录并支持以下最低功能:
  • 最常见文件的 MIME 类型
  • 提供 HTML、JS、CSS、纯文本和图像
  • 服务 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 相似的功能版本但使用稍微低级的 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 模块,它是 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 模块,它是 Node.js 中 TCP 套接字的最低级 API。

    此示例具有 http 的一些功能版本但最小和不完整的 HTTP 协议(protocol)是从头开始实现的。由于它不支持分块编码,因此在发送响应之前将文件加载到内存中以了解大小,因为统计文件然后加载会引入竞争条件。
    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 , httpnet :
  • https://github.com/rsp/node-static-http-servers

  • 其他项目仅使用 express.static :
  • https://github.com/rsp/node-express-static-example

  • 测试

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

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

    也可以看看

    其他相关回答:
  • Failed to load resource from same directory when redirecting Javascript
  • onload js call not working with node
  • Sending whole folder content to client with express
  • Loading partials fails on the server JS
  • Node JS not serving the static image
  • 关于node.js - 如何使用 nodejs 提供图像,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/5823722/

    有关node.js - 如何使用 nodejs 提供图像的更多相关文章

    1. ruby - 如何使用 Nokogiri 的 xpath 和 at_xpath 方法 - 2

      我正在学习如何使用Nokogiri,根据这段代码我遇到了一些问题:require'rubygems'require'mechanize'post_agent=WWW::Mechanize.newpost_page=post_agent.get('http://www.vbulletin.org/forum/showthread.php?t=230708')puts"\nabsolutepathwithtbodygivesnil"putspost_page.parser.xpath('/html/body/div/div/div/div/div/table/tbody/tr/td/div

    2. ruby - 如何从 ruby​​ 中的字符串运行任意对象方法? - 2

      总的来说,我对ruby​​还比较陌生,我正在为我正在创建的对象编写一些rspec测试用例。许多测试用例都非常基础,我只是想确保正确填充和返回值。我想知道是否有办法使用循环结构来执行此操作。不必为我要测试的每个方法都设置一个assertEquals。例如:describeitem,"TestingtheItem"doit"willhaveanullvaluetostart"doitem=Item.new#HereIcoulddotheitem.name.shouldbe_nil#thenIcoulddoitem.category.shouldbe_nilendend但我想要一些方法来使用

    3. ruby - 使用 RubyZip 生成 ZIP 文件时设置压缩级别 - 2

      我有一个Ruby程序,它使用rubyzip压缩XML文件的目录树。gem。我的问题是文件开始变得很重,我想提高压缩级别,因为压缩时间不是问题。我在rubyzipdocumentation中找不到一种为创建的ZIP文件指定压缩级别的方法。有人知道如何更改此设置吗?是否有另一个允许指定压缩级别的Ruby库? 最佳答案 这是我通过查看ruby​​zip内部创建的代码。level=Zlib::BEST_COMPRESSIONZip::ZipOutputStream.open(zip_file)do|zip|Dir.glob("**/*")d

    4. ruby - 为什么我可以在 Ruby 中使用 Object#send 访问私有(private)/ protected 方法? - 2

      类classAprivatedeffooputs:fooendpublicdefbarputs:barendprivatedefzimputs:zimendprotecteddefdibputs:dibendendA的实例a=A.new测试a.foorescueputs:faila.barrescueputs:faila.zimrescueputs:faila.dibrescueputs:faila.gazrescueputs:fail测试输出failbarfailfailfail.发送测试[:foo,:bar,:zim,:dib,:gaz].each{|m|a.send(m)resc

    5. ruby-on-rails - 使用 Ruby on Rails 进行自动化测试 - 最佳实践 - 2

      很好奇,就使用ruby​​onrails自动化单元测试而言,你们正在做什么?您是否创建了一个脚本来在cron中运行rake作业并将结果邮寄给您?git中的预提交Hook?只是手动调用?我完全理解测试,但想知道在错误发生之前捕获错误的最佳实践是什么。让我们理所当然地认为测试本身是完美无缺的,并且可以正常工作。下一步是什么以确保他们在正确的时间将可能有害的结果传达给您? 最佳答案 不确定您到底想听什么,但是有几个级别的自动代码库控制:在处理某项功能时,您可以使用类似autotest的内容获得关于哪些有效,哪些无效的即时反馈。要确保您的提

    6. ruby - 在 Ruby 中使用匿名模块 - 2

      假设我做了一个模块如下:m=Module.newdoclassCendend三个问题:除了对m的引用之外,还有什么方法可以访问C和m中的其他内容?我可以在创建匿名模块后为其命名吗(就像我输入“module...”一样)?如何在使用完匿名模块后将其删除,使其定义的常量不再存在? 最佳答案 三个答案:是的,使用ObjectSpace.此代码使c引用你的类(class)C不引用m:c=nilObjectSpace.each_object{|obj|c=objif(Class===objandobj.name=~/::C$/)}当然这取决于

    7. ruby - 使用 ruby​​ 和 savon 的 SOAP 服务 - 2

      我正在尝试使用ruby​​和Savon来使用网络服务。测试服务为http://www.webservicex.net/WS/WSDetails.aspx?WSID=9&CATID=2require'rubygems'require'savon'client=Savon::Client.new"http://www.webservicex.net/stockquote.asmx?WSDL"client.get_quotedo|soap|soap.body={:symbol=>"AAPL"}end返回SOAP异常。检查soap信封,在我看来soap请求没有正确的命名空间。任何人都可以建议我

    8. python - 如何使用 Ruby 或 Python 创建一系列高音调和低音调的蜂鸣声? - 2

      关闭。这个问题是opinion-based.它目前不接受答案。想要改进这个问题?更新问题,以便editingthispost可以用事实和引用来回答它.关闭4年前。Improvethisquestion我想在固定时间创建一系列低音和高音调的哔哔声。例如:在150毫秒时发出高音调的蜂鸣声在151毫秒时发出低音调的蜂鸣声200毫秒时发出低音调的蜂鸣声250毫秒的高音调蜂鸣声有没有办法在Ruby或Python中做到这一点?我真的不在乎输出编码是什么(.wav、.mp3、.ogg等等),但我确实想创建一个输出文件。

    9. ruby-on-rails - 如何验证 update_all 是否实际在 Rails 中更新 - 2

      给定这段代码defcreate@upgrades=User.update_all(["role=?","upgraded"],:id=>params[:upgrade])redirect_toadmin_upgrades_path,:notice=>"Successfullyupgradeduser."end我如何在该操作中实际验证它们是否已保存或未重定向到适当的页面和消息? 最佳答案 在Rails3中,update_all不返回任何有意义的信息,除了已更新的记录数(这可能取决于您的DBMS是否返回该信息)。http://ar.ru

    10. ruby-on-rails - 'compass watch' 是如何工作的/它是如何与 rails 一起使用的 - 2

      我在我的项目目录中完成了compasscreate.和compassinitrails。几个问题:我已将我的.sass文件放在public/stylesheets中。这是放置它们的正确位置吗?当我运行compasswatch时,它不会自动编译这些.sass文件。我必须手动指定文件:compasswatchpublic/stylesheets/myfile.sass等。如何让它自动运行?文件ie.css、print.css和screen.css已放在stylesheets/compiled。如何在编译后不让它们重新出现的情况下删除它们?我自己编译的.sass文件编译成compiled/t

    随机推荐