Why isn't node serving my image file? - node.js

I have a vanilla node.js http server. Everything except my image file works. I just get the broken image icon on the page.
Here is my server code:
"use strict";
class app {
constructor() {
app.loadServer();
}
static loadServer() {
const HTTP = require('http'),
PORT = 1337,
SERVER = HTTP.createServer(function(req, res) {
let httpHandler = function(err, str, contentType) {
console.log('\n\n' + 'Content type: ' + contentType + '\n\n');
if (err) {
res.writeHead(500, {'Content-Type': 'text/plain'});
res.end('An error has occurred: ' + err.message);
} else if (contentType.indexOf('image') >= 0) {
res.writeHead(200, { 'Content-Type': contentType });
res.end(str, 'binary');
} else {
res.writeHead(200, { 'Content-Type': contentType });
res.end(str);
}
};
if (req.headers['x-requested-with'] === 'XMLHttpRequest') {
if (req.method == 'POST') {
app.getFormData(req, res);
} else {
console.log("[405] " + req.method + " to " + req.url);
res.writeHead(405, "Method not supported", { 'Content-Type': 'text/html' });
res.end('<html><head><title>405 - Method not supported</title></head><body><h1>Method not supported.</h1></body></html>');
}
} else if (req.url.indexOf('/javascripts/') >= 0) {
app.render(req.url.slice(1), 'application/ecmascript', httpHandler);
} else if (req.url.indexOf('/css/') >= 0) {
app.render(req.url.slice(1), 'text/css', httpHandler);
} else if (req.url.indexOf('/images/') >= 0) {
app.render(req.url.slice(1), 'image/jpg', httpHandler);
} else {
app.render('public/views/index.html', 'text/html', httpHandler);
}
}).listen(PORT, function() {
console.log('-= Francis Server Listening at http://127.0.0.1:' + PORT + ' =-');
});
}
static render(path, contentType, callback) {
const FS = require('fs');
FS.readFile(__dirname + '/' + path, 'utf-8', function(err, str) {
callback(err, str, contentType);
});
}
static getFormData(req, res) {
const FORMIDABLE = require('formidable'),
DO_NAMES = require('./node/NameClass');
let formData = {};
new FORMIDABLE.IncomingForm().parse(req)
.on('field', function(field, name) {
formData[field] = name;
})
.on('error', function(err) {
next(err);
})
.on('end', function() {
let finalName = new DO_NAMES(formData);
res.writeHead(200, {'content-type': 'text/plain'});
res.write('-= Received form: ');
res.end(finalName.getFirstName() + ' ' + finalName.getLastName());
});
}
}
module.exports = app;
It feels like it's trying to serve the image as text instead of picture. I verified that the image is there and readable.

I found the problem.
it happens here:
FS.readFile(__dirname + '/' + path, 'utf-8', function(err, str) {
callback(err, str, contentType);
});
You read the image file as UTF-8 but it is a binary file. That is why the image data is corrupt. Instead you have to use binary as encoding.
You could change your code like this:
static render(path, contentType, callback, encoding) {
const FS = require('fs');
FS.readFile(__dirname + '/' + path, encoding ? encoding : 'utf-8', function(err, str) {
callback(err, str, contentType);
});
}
and then call render like this:
app.render(req.url.slice(1), 'image/jpeg', httpHandler, 'binary');
There are obviously better ways to do it but this requires a minimum amount of change to your code. Just make sure the readFile() encoding is binary for binary files.
Also the correct mime type for jpg is image/jpeg not image/jpg. Most, if not all, browsers won't care but it is more clean.

It looks like your NODE server is setting the wrong MIME type. You can set the MIME type yourself, as you are doing, but this gets awfully painful. I would recommend using a MIME type node module that is made for this exact purpose.
https://www.npmjs.com/package/mime
This npm package does exactly this with very little effort.

Related

echosign combinedDocument api

https://api.na1.echosign.com/api/rest/v5/agreements/{agreementId}/combinedDocument
I am trying to create a file from the body of the response, but it is creating a file that I can't open. It requires a password even though there isn't one on the file. I think this must have something to do with the encoding / decoding.
I am using a node express server. Here are the few lines of code I am using:
var request = require('request');
request({
baseUrl: 'https://api.na1.echosign.com/api/rest/v5',
url: '/agreements/' + req.params.id + '/combinedDocument',
headers: {'Access-Token': process.env.ECHOSIGN_INTEGRATIONKEY}
},
function(error, response, body){
if(error) {
res.send(error);
}
else {
var buf = new Buffer(body)
res.set({
'Content-Disposition': 'attachment; filename=test.pdf',
'Content-Type': 'application/pdf; charset=utf-8'
});
res.write(buf);
res.end();
}
}
);
This is what ended up working in the end in case somebody else stumbles across this. I think the problem was that the data being returned from the API is a stream and it needed to have the chunking logic and then get concatenated in order to avoid getting corrupted.
Also included is encoding to base64, pushing it into a database and then getting it back, decoding it and pushing it to the browser. I wasn't going to leave it like that, but had it set up that way to test the full cycle.
router.get('/echosign/agreement/:id', function(req, res) {
if (req.user !== 'myUserId') {
console.log(req.user);
res.redirect('/');
} else {
var request = require('request');
var data = [];
request({
baseUrl: 'https://api.na1.echosign.com/api/rest/v5',
url: '/agreements/' + req.params.id + '/combinedDocument',
headers: {'Access-Token': process.env.ECHOSIGN_INTEGRATIONKEY}
}).on('data', function(chunk){
data.push(chunk);
})
.on('end', function(){
data = Buffer.concat(data).toString('base64');
client.connect(function(err) {
if(err) {
return console.error('could not connect to postgres', err);
}
client.query("UPDATE agreements SET file = '" + data + "' WHERE agreementid = '" + req.params.id + "' RETURNING agreement, file", function(err, result) {
if(err) {
return console.log(result, err);
}
client.end();
res.set({
'Content-Type': 'application/pdf;charset=UTF-8',
'Content-Disposition': "inline; filename='" + result.rows[0].agreement.name + ".pdf'"
});
res.send(new Buffer(result.rows[0].file, 'base64'));
});
});
});
}
});

Node JS Request + Express Pipe

I have a problem with streaming video files from a server to another.
I wrote this script
var request = require("request"),
express = require("express");
var app = express();
app.get("/cast", function(req, res) {
var url = req.query.video;
res.writeHead(200, {
'Content-Type': 'video/mp4'
});
request({
url: url,
headers: {
Referer: "http://example.com/1706398/" + url
}
})
.on('response', function(response) {
response.on('data', function(data) {
console.log("data chunk received: " + data.length)
});
response.on('end', function(data) {
console.log('Video completed');
});
})
.pipe(res);
});
app.listen(8080);
But video response sometimes works sometimes is corrupted, instead if request's data is written in a writeable buffer and saved as video file it works with any url.
I cannot found any error or problem in my code, here some urls :
Here some url that I tryed:
https://gist.github.com/FrancisCan/f2bb86f8ff73b45fa192
Thanks :)
Remove the writeHead 200, when you are streaming, you should return http 206 results back (partial content), and not http200. I have the same scenario as you (streaming a video file from a blob container in the cloud to an angular application), there is no need for the http200 response.
Update: adding some code on how I do it:
AzureStorageHelper.prototype.streamBlob = function streamBlob(req, res, blob, params) {
if(!params){
params.container = container;
}
blob_service.getBlobProperties(params.container, blob, function (error, result, response) {
if(!result) return res.status(404).end();
if(error) return res.status(500).end();
var blobLength = result.contentLength;
var range = req.headers.range ? req.headers.range : "bytes=0-";
var positions = range.replace(/bytes=/, "").split("-");
var start = parseInt(positions[0], 10);
var end = positions[1] ? parseInt(positions[1], 10) : blobLength - 1;
var chunksize = (end - start) + 1;
var options = {
rangeStart: start,
rangeEnd: end,
}
//this is what's interesting for your scenario. I used to set this up myself but it's is now handled by the Azure Storage NodejsSDK
/*res.writeHead(206, {
'Accept-Ranges': 'bytes',
'Content-Range': "bytes " + start + "-" + end + "/" + blobLength,
'Content-Type': result.contentType,
'Content-Length': chunksize,
'Content-MD5': result.contentMD5,
});*/
var options = {
rangeStart: start,
rangeEnd: end,
}
//this is an API from the Azure Storage nodejsSDK I use. You might want to check the source of this method in github to see how this lib deals with the http206 responses and the piping
blob_service.getBlobToStream(params.container, blob, res, options, function (error, result, response) {
if (error) {
return res.status(500).end();
}
});
});

Serving css, js files in Node.js without frameworks like Express

This question has probably been asked earlier but the answers are not very convincing.
I have the following code with me -
var http = require('http'),
fs = require('fs');
connect = require('connect');
function serveStaticFile(res, path, contentType, responseCode) {
if(!responseCode) responseCode = 200;
fs.readFile(__dirname + path, function(err,data) {
if(err) {
res.writeHead(500, { 'Content-Type': 'text/plain' });
res.end('500 - Internal Error');
} else {
res.writeHeader(responseCode,
{ 'Content-Type': contentType });
res.write(data)
res.end();
}
});
}
http.createServer(function(req,res){
// normalize url by removing querystring, optional
// trailing slash, and making lowercase
var path = req.url.replace(/\/?(?:\?.*)?$/, '')
.toLowerCase();
switch(path) {
case '': serveStaticFile(res, '/public/index.html','texthtml');
break;
default:
serveStaticFile(res, '/public/404.html', 'text/html',
404);
break;
}
}).listen(3000);
console.log('Server started on localhost:3000; press Ctrl-C to terminate....');
I am just not able to load the css and js files mentioned in the index.js. What am I doing wrong? If I open index.js in the browser directly, the css and js load fine.
Express uses serve-static for serving static directories which is uses send for streaming files. You can use that instead of express, or take at a look their implementation.
What are you think about send ? Use that like this:
function serveStaticFile(res, path, contentType, responseCode) {
if(!responseCode) responseCode = 200;
res.status = responseCode;
function error(err) {
res.statusCode = err.status || 500;
res.end(err.message);
}
function headers(res, path, stat) {
res.setHeader('Content-type', contentType);
}
send(req, path, {root: __dirname})
.on('error', error)
.on('headers', headers)
.pipe(res);
}

nodejs proxy local api calls to external rest api service (no express.js)

I'm running the demo app of angularjs (angular-seed) using nodejs with the default project file /scripts/web-server.js (see below)
I would like node js to proxy/redirect all the local calls to /api to the external Rest api endpoint http://www.mywebsite.com/api in order to avoid the cross domain origin policy.
How should I edit the web-server.js below to achieve the proxy redirect?
All the example I found do use express.js. I'm using node.js only as development environment so I've no interest in using express.js.
Default web-server.js nodejs script:
#!/usr/bin/env node
var util = require('util'),
http = require('http'),
fs = require('fs'),
url = require('url'),
events = require('events'),
request = require('request');
;
var DEFAULT_PORT = 8000;
function main(argv) {
new HttpServer({
'GET': createServlet(StaticServlet),
'HEAD': createServlet(StaticServlet)
}).start(Number(argv[2]) || DEFAULT_PORT);
}
function escapeHtml(value) {
return value.toString().
replace('', '>').
replace('"', '"');
}
function createServlet(Class) {
var servlet = new Class();
return servlet.handleRequest.bind(servlet);
}
/**
* An Http server implementation that uses a map of methods to decide
* action routing.
*
* #param {Object} Map of method => Handler function
*/
function HttpServer(handlers) {
this.handlers = handlers;
this.server = http.createServer(this.handleRequest_.bind(this));
}
HttpServer.prototype.start = function(port) {
this.port = port;
this.server.listen(port);
util.puts('Http Server running at http://localhost:' + port + '/');
};
HttpServer.prototype.parseUrl_ = function(urlString) {
var parsed = url.parse(urlString);
parsed.pathname = url.resolve('/', parsed.pathname);
return url.parse(url.format(parsed), true);
};
HttpServer.prototype.handleRequest_ = function(req, res) {
var logEntry = req.method + ' ' + req.url;
if (req.headers['user-agent']) {
logEntry += ' ' + req.headers['user-agent'];
}
util.puts(logEntry);
req.url = this.parseUrl_(req.url);
var handler = this.handlers[req.method];
if (!handler) {
res.writeHead(501);
res.end();
} else {
handler.call(this, req, res);
}
};
/**
* Handles static content.
*/
function StaticServlet() {}
StaticServlet.MimeMap = {
'txt': 'text/plain',
'html': 'text/html',
'css': 'text/css',
'xml': 'application/xml',
'json': 'application/json',
'js': 'application/javascript',
'jpg': 'image/jpeg',
'jpeg': 'image/jpeg',
'gif': 'image/gif',
'png': 'image/png',
  'svg': 'image/svg+xml'
};
StaticServlet.prototype.handleRequest = function(req, res) {
var self = this;
var path = ('./' + req.url.pathname).replace('//','/').replace(/%(..)/g, function(match, hex){
return String.fromCharCode(parseInt(hex, 16));
});
var parts = path.split('/');
if (parts[parts.length-1].charAt(0) === '.')
return self.sendForbidden_(req, res, path);
fs.stat(path, function(err, stat) {
if (err)
return self.sendMissing_(req, res, path);
if (stat.isDirectory())
return self.sendDirectory_(req, res, path);
return self.sendFile_(req, res, path);
});
}
StaticServlet.prototype.sendError_ = function(req, res, error) {
res.writeHead(500, {
'Content-Type': 'text/html'
});
res.write('\n');
res.write('Internal Server Error\n');
res.write('Internal Server Error');
res.write('' + escapeHtml(util.inspect(error)) + '');
util.puts('500 Internal Server Error');
util.puts(util.inspect(error));
};
StaticServlet.prototype.sendMissing_ = function(req, res, path) {
path = path.substring(1);
res.writeHead(404, {
'Content-Type': 'text/html'
});
res.write('\n');
res.write('404 Not Found\n');
res.write('Not Found');
res.write(
'The requested URL ' +
escapeHtml(path) +
' was not found on this server.'
);
res.end();
util.puts('404 Not Found: ' + path);
};
StaticServlet.prototype.sendForbidden_ = function(req, res, path) {
path = path.substring(1);
res.writeHead(403, {
'Content-Type': 'text/html'
});
res.write('\n');
res.write('403 Forbidden\n');
res.write('Forbidden');
res.write(
'You do not have permission to access ' +
escapeHtml(path) + ' on this server.'
);
res.end();
util.puts('403 Forbidden: ' + path);
};
StaticServlet.prototype.sendRedirect_ = function(req, res, redirectUrl) {
res.writeHead(301, {
'Content-Type': 'text/html',
'Location': redirectUrl
});
res.write('\n');
res.write('301 Moved Permanently\n');
res.write('Moved Permanently');
res.write(
'The document has moved here.'
);
res.end();
util.puts('301 Moved Permanently: ' + redirectUrl);
};
StaticServlet.prototype.sendFile_ = function(req, res, path) {
var self = this;
var file = fs.createReadStream(path);
res.writeHead(200, {
'Content-Type': StaticServlet.
MimeMap[path.split('.').pop()] || 'text/plain'
});
if (req.method === 'HEAD') {
res.end();
} else {
file.on('data', res.write.bind(res));
file.on('close', function() {
res.end();
});
file.on('error', function(error) {
self.sendError_(req, res, error);
});
}
};
StaticServlet.prototype.sendDirectory_ = function(req, res, path) {
var self = this;
if (path.match(/[^\/]$/)) {
req.url.pathname += '/';
var redirectUrl = url.format(url.parse(url.format(req.url)));
return self.sendRedirect_(req, res, redirectUrl);
}
fs.readdir(path, function(err, files) {
if (err)
return self.sendError_(req, res, error);
if (!files.length)
return self.writeDirectoryIndex_(req, res, path, []);
var remaining = files.length;
files.forEach(function(fileName, index) {
fs.stat(path + '/' + fileName, function(err, stat) {
if (err)
return self.sendError_(req, res, err);
if (stat.isDirectory()) {
files[index] = fileName + '/';
}
if (!(--remaining))
return self.writeDirectoryIndex_(req, res, path, files);
});
});
});
};
StaticServlet.prototype.writeDirectoryIndex_ = function(req, res, path, files) {
path = path.substring(1);
res.writeHead(200, {
'Content-Type': 'text/html'
});
if (req.method === 'HEAD') {
res.end();
return;
}
res.write('\n');
res.write('' + escapeHtml(path) + '\n');
res.write('\n');
res.write(' ol { list-style-type: none; font-size: 1.2em; }\n');
res.write('\n');
res.write('Directory: ' + escapeHtml(path) + '');
res.write('');
files.forEach(function(fileName) {
if (fileName.charAt(0) !== '.') {
res.write('' +
escapeHtml(fileName) + '');
}
});
res.write('');
res.end();
};
// Must be last,
main(process.argv);

Empty HTTP response

For some reason, this code keeps failing :
var http = require('http');
var url = require ('url');
var jade = require('jade');
var fs = require('fs');
http.createServer(function(req, res) {
// Request handlers
var path = url.parse(req.url, true).pathname;
var dot = path.lastIndexOf('.');
var extension = path.substr(dot + 1);
// Response handlers
switch(extension) {
case 'css' :
res.writeHeader(200, {"Content-Type": "text/css"});
fs.readFile(path.substr(1, dot - 1) + '.css', function (err, css) {
if(err) {
res.write(err, 'utf8')
};
res.write(css, 'utf8')
});
break;
case 'js' :
res.writeHeader(200, {"Content-Type": "text/javascript"});
fs.readFile(path.substr(1, dot - 1) + '.js', function (err, js) {
if(err) {
res.write(err, 'utf8')
};
res.write(js, 'utf8')
});
break;
default :
res.writeHeader(200, {"Content-Type": "text/html"});
jade.renderFile(path.substr(1, path.length) + '.jade', { title: 'test' }, function(err, html) {
if(err) {
res.write(err, 'utf8')
};
res.write(html, 'utf8');
});
break;
}
res.end();
}).listen(8080);
I don't get any errors, it's just that the responses seem to be empty.
There is a header but no css or javascript file.
The way I link them in Jade is the following :
link(rel='stylesheet', href='/css/bootstrap.min.css')
link(rel='stylesheet', href='/css/style.css')
Thank you in advance !
You've called res.end() too soon. Instead, put it after the last res.write() in each of your fs.readFile handlers.

Resources