programing

NodeJS의 기본 정적 파일 서버

nasanasas 2020. 9. 19. 11:22
반응형

NodeJS의 기본 정적 파일 서버


완벽한 서버보다는 노드를 이해하기위한 연습으로 nodejs에서 정적 파일 서버를 만들려고합니다. 저는 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. 노드의 .pipe ()는 기본적으로 다음을 수행합니까?

.

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

모두 감사합니다!


  • 다음을 제외하고 기본 서버는 괜찮아 보입니다.

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

  • Yes pipe() does basically that, it also pauses/resumes the source stream (in case the receiver is slower). Here is the source code of the pipe() function: https://github.com/joyent/node/blob/master/lib/stream.js


Less is more

Just go command prompt first on your project and use

$ npm install express

Then write your app.js code like so:

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

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

You would then create a "public" folder where you place your files. I tried it the harder way first but you have to worry about mime types which is just having to map stuff which is time consuming and then worry about response types, etc. etc. etc.... no thank you.


I like understanding what's going on under the hood as well.

I noticed a few things in your code that you probably want to clean up:

  • It crashes when filename points to a directory, because exists is true and it tries to read a file stream. I used fs.lstatSync to determine directory existence.

  • It isn't using the HTTP response codes correctly (200, 404, etc)

  • While MimeType is being determined (from the file extension), it isn't being set correctly in res.writeHead (as stewe pointed out)

  • To handle special characters, you probably want to unescape the uri

  • It blindly follows symlinks (could be a security concern)

Given this, some of the apache options (FollowSymLinks, ShowIndexes, etc) start to make more sense. I've update the code for your simple file server as follows:

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

How about this pattern, which avoids checking separately that the file exists

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

I made a httpServer function with extra features for general usage based on @Jeff Ward answer

  1. custtom dir
  2. index.html returns if req === dir

Usage:

httpServer(dir).listen(port);

https://github.com/kenokabe/ConciseStaticHttpServer

Thanks.


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]))

the st module makes serving static files easy. Here is an extract of 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)

@JasonSebring answer pointed me in the right direction, however his code is outdated. Here is how you do it with the newest connect version.

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

In connect GitHub Repository there are other middlewares you can use.

참고URL : https://stackoverflow.com/questions/7268033/basic-static-file-server-in-nodejs

반응형