【问题标题】:Basic static file server in NodeJSNodeJS 中的基本静态文件服务器
【发布时间】:2011-11-08 05:49:36
【问题描述】:

我正在尝试在 nodejs 中创建一个静态文件服务器,而不是作为一个完美的服务器来理解 node。我非常了解 Connect 和 node-static 之类的项目,并且完全打算将这些库用于更多可用于生产的代码,但我也想了解我正在使用的基础知识。考虑到这一点,我编写了一个小型 server.js:

var http = require('http'),
    url = require('url'),
    path = require('path'),
    fs = require('fs');
var mimeTypes = {
    "html": "text/html",
    "jpeg": "image/jpeg",
    "jpg": "image/jpeg",
    "png": "image/png",
    "js": "text/javascript",
    "css": "text/css"};

http.createServer(function(req, res) {
    var uri = url.parse(req.url).pathname;
    var filename = path.join(process.cwd(), uri);
    path.exists(filename, function(exists) {
        if(!exists) {
            console.log("not exists: " + filename);
            res.writeHead(200, {'Content-Type': 'text/plain'});
            res.write('404 Not Found\n');
            res.end();
        }
        var mimeType = mimeTypes[path.extname(filename).split(".")[1]];
        res.writeHead(200, mimeType);

        var fileStream = fs.createReadStream(filename);
        fileStream.pipe(res);

    }); //end path.exists
}).listen(1337);

我的问题是双重的

  1. 这是在节点中创建和流式传输基本 html 等的“正确”方法,还是有更好/更优雅/更健壮的方法?

  2. node中的.pipe()基本上只是在做下面的事情吗?

.

var fileStream = fs.createReadStream(filename);
fileStream.on('data', function (data) {
    res.write(data);
});
fileStream.on('end', function() {
    res.end();
});

谢谢大家!

【问题讨论】:

  • 我写了一个模块,可以让你在不影响灵活性的情况下做到这一点。它还会自动缓存您的所有资源。看看:github.com/topcloud/cachemere
  • 您选择(?)返回“404 Not Found”和 HTTP 状态代码“200 OK”有点有趣。如果在 URL 上找不到资源,那么适当的代码应该是 404(您在文档正文中写的内容通常是次要的)。否则你会混淆很多用户代理(包括网络爬虫和其他机器人)给他们的文档没有真正的价值(他们也可能缓存)。
  • 谢谢。多年后仍然很好地工作。
  • 谢谢!这段代码运行良好。但是现在在上面的代码中使用fs.exists() 而不是path.exists()。干杯!是的!不要忘记return:
  • 注意1) fs.exists()弃用。使用fs.access() 或更好的上述用例fs.stat()2) url.parse弃用;请改用较新的new URL 接口。

标签: http node.js webserver


【解决方案1】:

少即是多

只需在您的项目上先进入命令提示符并使用

$ npm install express

然后像这样编写你的 app.js 代码:

var express = require('express'),
app = express(),
port = process.env.PORT || 4000;

app.use(express.static(__dirname + '/public'));
app.listen(port);

然后,您将创建一个“公共”文件夹来放置文件。我首先尝试了更难的方法,但是您必须担心 mime 类型,它只需要映射耗时的东西,然后担心响应类型等等等....不,谢谢。

【讨论】:

  • +1 使用经过测试的代码而不是自己编写代码有很多话要说。
  • 我尝试查看文档,但似乎找不到太多内容,您能解释一下您的 sn-p 在做什么吗?我尝试使用这种特殊的变体,但我不知道可以用什么替换。
  • 如果您想要目录列表,只需在 connect.static 行之后添加 .use(connect.directory('public')) ,将 public 替换为您的路径。很抱歉劫持,但我认为它为我解决了问题。
  • 你不妨'使用jQuery'!这不是对 OP 问题的回答,而是对甚至不存在的问题的解决方案。 OP 表示这个实验的目的是学习 Node。
  • @JasonSebring 为什么require('http') 在第二行?
【解决方案2】:
  • 您的基本服务器看起来不错,除了:

    缺少return 语句。

    res.write('404 Not Found\n');
    res.end();
    return; // <- Don't forget to return here !!
    

    还有:

    res.writeHead(200, mimeType);

    应该是:

    res.writeHead(200, {'Content-Type':mimeType});

  • 是的pipe() 基本上是这样做的,它还会暂停/恢复源流(以防接收器速度较慢)。 这里是pipe()函数的源代码:https://github.com/joyent/node/blob/master/lib/stream.js

【讨论】:

  • 如果文件名像 blah.blah.css 会发生什么?
  • mimeType 在那种情况下应该是 blah xP
  • 这不是问题吗?如果你自己写,你就是在问这些类型的错误。很好的学习练习,但我正在学习欣赏“连接”而不是自己动手。这个页面的问题是人们只是想知道如何做一个简单的文件服务器,堆栈溢出首先出现。这个答案是正确的,但人们并不在寻找它,只是一个简单的答案。我必须自己找出更简单的,所以把它放在这里。
  • +1 用于不以库的形式粘贴指向解决方案的链接,而是实际编写问题的答案。
【解决方案3】:

我也喜欢了解幕后发生的事情。

我注意到你的代码中有一些你可能想要清理的东西:

  • 当文件名指向一个目录时它会崩溃,因为存在为真并且它试图读取一个文件流。我使用 fs.lstatSync 来确定目录是否存在。

  • 未正确使用 HTTP 响应代码(200、404 等)

  • 在确定 MimeType 时(从文件扩展名),它在 res.writeHead 中的设置不正确(正如stete 指出的那样)

  • 要处理特殊字符,您可能需要取消转义 uri

  • 它盲目地遵循符号链接(可能是一个安全问题)

鉴于此,一些 apache 选项(FollowSymLinks、ShowIndexes 等)开始变得更有意义。我已将您的简单文件服务器的代码更新如下:

var http = require('http'),
    url = require('url'),
    path = require('path'),
    fs = require('fs');
var mimeTypes = {
    "html": "text/html",
    "jpeg": "image/jpeg",
    "jpg": "image/jpeg",
    "png": "image/png",
    "js": "text/javascript",
    "css": "text/css"};

http.createServer(function(req, res) {
  var uri = url.parse(req.url).pathname;
  var filename = path.join(process.cwd(), unescape(uri));
  var stats;

  try {
    stats = fs.lstatSync(filename); // throws if path doesn't exist
  } catch (e) {
    res.writeHead(404, {'Content-Type': 'text/plain'});
    res.write('404 Not Found\n');
    res.end();
    return;
  }


  if (stats.isFile()) {
    // path exists, is a file
    var mimeType = mimeTypes[path.extname(filename).split(".").reverse()[0]];
    res.writeHead(200, {'Content-Type': mimeType} );

    var fileStream = fs.createReadStream(filename);
    fileStream.pipe(res);
  } else if (stats.isDirectory()) {
    // path exists, is a directory
    res.writeHead(200, {'Content-Type': 'text/plain'});
    res.write('Index of '+uri+'\n');
    res.write('TODO, show index?\n');
    res.end();
  } else {
    // Symbolic link, other?
    // TODO: follow symlinks?  security?
    res.writeHead(500, {'Content-Type': 'text/plain'});
    res.write('500 Internal server error\n');
    res.end();
  }

}).listen(1337);

【讨论】:

  • 我可以建议“var mimeType = mimeTypes[path.extname(filename).split(".").reverse()[0]];”反而?一些文件名有多个“。”例如“my.cool.video.mp4”或“download.tar.gz”
  • 这是否会以某种方式阻止某人使用文件夹/../../../home/user/jackpot.privatekey 之类的网址?我看到连接以确保路径在下游,但我想知道使用 ../../../ 类型的表示法是否可以解决这个问题。也许我会自己测试一下。
  • 它不起作用。我不知道为什么,但很高兴知道。
  • 不错,RegEx匹配也可以收集扩展名; var mimeType = mimeTypes[path.extname(filename).match(/\.([^\.]+)$/)[1]];
【解决方案4】:
var http = require('http')
var fs = require('fs')

var server = http.createServer(function (req, res) {
  res.writeHead(200, { 'content-type': 'text/plain' })

  fs.createReadStream(process.argv[3]).pipe(res)
})

server.listen(Number(process.argv[2]))

【讨论】:

  • 可能想多解释一下。
【解决方案5】:

这个模式怎么样,它避免单独检查文件是否存在

        var fileStream = fs.createReadStream(filename);
        fileStream.on('error', function (error) {
            response.writeHead(404, { "Content-Type": "text/plain"});
            response.end("file not found");
        });
        fileStream.on('open', function() {
            var mimeType = mimeTypes[path.extname(filename).split(".")[1]];
            response.writeHead(200, {'Content-Type': mimeType});
        });
        fileStream.on('end', function() {
            console.log('sent file ' + filename);
        });
        fileStream.pipe(response);

【讨论】:

  • 你忘记了 mimetype 以防成功。我正在使用这种设计,但不是立即管道流,而是在文件流的“打开”事件中管道它们:mimetype 的 writeHead,然后管道。不需要结尾:readable.pipe.
  • 根据@GeH 的评论修改。
  • 应该是fileStream.on('open', ...
【解决方案6】:

我根据@Jeff Ward 的回答制作了一个带有额外功能的 httpServer 函数以供一般使用

  1. 自定义目录
  2. 如果 req === dir 则返回 index.html

用法:

httpServer(dir).listen(port);

https://github.com/kenokabe/ConciseStaticHttpServer

谢谢。

【讨论】:

【解决方案7】:

st module 使提供静态文件变得容易。这是 README.md 的摘录:

var mount = st({ path: __dirname + '/static', url: '/static' })
http.createServer(function(req, res) {
  var stHandled = mount(req, res);
  if (stHandled)
    return
  else
    res.end('this is not a static file')
}).listen(1338)

【讨论】:

    【解决方案8】:

    @JasonSebring 的回答为我指明了正确的方向,但是他的代码已经过时了。这是使用最新的connect 版本的方法。

    var connect = require('connect'),
        serveStatic = require('serve-static'),
        serveIndex = require('serve-index');
    
    var app = connect()
        .use(serveStatic('public'))
        .use(serveIndex('public', {'icons': true, 'view': 'details'}))
        .listen(3000);
    

    connectGitHub Repository还有其他中间件可以使用。

    【讨论】:

    • 我只是使用 express 来获得更简单的答案。最新的 express 版本具有静态功能,但仅此而已。谢谢!
    • 查看connect文档,它只是middlewarewrapper。所有其他有趣的middleware 都来自express 存储库,因此从技术上讲,您可以使用express.use() 来使用这些API。
    猜你喜欢
    • 2013-12-08
    • 2020-05-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-11-17
    • 2013-12-16
    • 2011-06-24
    相关资源
    最近更新 更多