[node.js] nodejs를 사용하여 이미지를 제공하는 방법

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; 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 년이 지난 후에는 일반 Henry가 정답하나만 제시 했지만 코드에 아무런 문제가 없지만 수신에 문제가있는 것 같습니다 . 그것은 논평했다 “다른 사람이 작업을 수행하려면에 의존하는 방법보다 훨씬 다른 설명하지 않습니다” 많은 사람들이 얼마나 사실은 많은 것들을 설명이 필요하다는 것을 분명하게 보여줍니다이 댓글을 투표했다.

우선 “Node.js를 사용하여 이미지를 제공하는 방법”에 대한 정답은 정적 파일 서버를 처음부터 구현하지 않고 잘못 수행하는 것입니다. 정답은 올바르게 작동하는 Express와 같은 모듈을 사용하는 입니다.

Express를 사용하는 것이 “다른 사람을 의지하여 작업을 수행하는 방법 외에는 다른 설명이 많지 않다”는 의견에 응답 해야합니다. http모듈 을 사용하면 다른 사람이 작업을 완료하기 위해 이미 의존 하고 있다는 점에 유의해야합니다 . 누군가가 일을 끝내기 위해 누군가에게 의존하고 싶지 않다면 적어도 원시 TCP 소켓을 대신 사용해야합니다. 아래 예제 중 하나에서 수행합니다.

더 심각한 문제는 여기서 http모듈 을 사용하는 모든 답변 이 망가 졌다는 것 입니다. 그들은 소개 경쟁 조건 , 안전하지 않은 경로 해상도 로 이어질 것입니다 경로 탐색 취약점 , 차단을 I / O 완전히 것 어떤 동시 요청 역할을하지 못하는 모든 다른 미묘한 문제에서을 – 그들이 완전히 질문에 대한 요구하시는의 예 깨진하고, 그러나 그들은 이미 httpTCP 소켓을 사용하는 대신 모듈 이 제공하는 추상화를 사용하므로 처음부터 모든 것을 요구하지 않습니다.

질문이 “학습 연습으로서 처음부터 정적 파일 서버를 구현하는 방법”이라면, 어떻게해야하는지에 대한 답변이 반드시 게시되어야합니다. 그러나 그럼에도 불구하고 우리는 적어도 정확해야합니다 . 또한 이미지를 제공하려는 사람이 나중에 더 많은 이미지를 제공하기를 원한다고 가정하는 것은 무리가 없으므로 하드 코딩 된 경로를 가진 단일 파일 만 제공 할 수있는 특정 사용자 정의 정적 파일 서버를 작성하는 것이 다소 근시안적입니다. 이미지를 제공하는 방법에 대한 답변을 검색하는 사람은 모든 이미지를 제공하는 일반적인 솔루션 대신 단일 이미지를 제공하는 솔루션에 만족한다고 생각하기 어렵습니다.

간단히 말해서, 이미지를 제공하는 방법과 그에 대한 대답은 적절한 모듈을 사용하여 전문 노드 의 모범 사례 를 사용하면서 읽을 수 있고 유지 보수 가능하며 미래를 보장 하는 안전하고 예비 적이며 신뢰할 수있는 방식으로 수행 하는 것입니다 개발. 그러나 나는 그러한 대답에 큰 도움이되는 것은 동일한 기능을 수동으로 구현하는 방법을 보여 주지만 슬프게도 지금까지 실패한 모든 시도는 실패한다는 데 동의합니다. 그렇기 때문에 새로운 예를 썼습니다.

이 짧은 소개 후에 5 가지 다른 레벨의 추상화 작업을하는 5 가지 예가 있습니다.

최소 기능

모든 예제는 public디렉토리 에서 파일을 제공하고 다음 과 같은 최소 기능을 지원합니다.

  • 가장 일반적인 파일의 MIME 유형
  • HTML, JS, CSS, 일반 텍스트 및 이미지 제공
  • 역할을 index.html기본 디렉토리 인덱스로
  • 누락 된 파일에 대한 오류 코드로 응답
  • 경로 탐색 취약점 없음
  • 파일을 읽는 동안 경쟁 조건이 없음

노드 버전 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

이 버전은 connect1보다 낮은 추상화 수준 인 모듈을 사용합니다 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

이 버전은 httpNode에서 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

이 버전은 netNode에서 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, httpnet:

다음을 사용하는 다른 프로젝트 express.static:

테스트

Travis에서 테스트 결과를 확인할 수 있습니다.

모든 것은 노드 버전 4, 5, 6 및 7에서 테스트되었습니다.

또한보십시오

다른 관련 답변 :


답변

다른 포스터에 동의합니다. 결국 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');


답변

Express 프레임 워크를 사용해야합니다 .

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 가 작동합니다.


답변

그것은 너무 늦기하지만 누군가, 내가 사용하는 데 도움이 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:7000/images/abc.jpg


app.listen(7000);


답변

요청 된 바닐라 노드 버전 :

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


답변

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


답변

REST 서비스에 Restify 를 사용하는 것을 좋아 합니다. 필자의 경우 이미지를 제공하기 위해 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();
}