Open pdf files in simple Node.js web page - node.js

I am trying to create a web page in node.js which lists all pdf files in a folder defined by a variable in the URL. When a user clicks on one pdf link, the file should open.
Sadly, being a total node.js/javascript beginner, I can't figure out how to do it.
URL: http://127.0.0.1:3000/?id=1001
OS: Windows Server 2012 R2
Steps which should be done:
The "id" variable has to be read from the URL
Then, only the .PDF files in the directory ./files/id/ have to be read and listed -- where "id" is the variable from 1.
Then, when the user clicks one of them, it should open in the browser (Chrome)
What I managed to do:
set up the web server
parse the URL for the id value
dir the id folder
display the files
Below the code:
const
http = require('http'),
hostname = '127.0.0.1',
port = 3000,
querystring = require('querystring'),
url = require('url'),
fs = require('fs');
const server = http.createServer((req, res) => {
const params = querystring.parse(url.parse(req.url).query);
if ('id' in params) {
let html = "Test page\n"
html += "id: " + params.id + "\n";
const pdfdir = "./files/" + params.id + "/";
console.log(pdfdir);
let files = fs.readdirSync(pdfdir);
files.forEach(file => {
const filepath = __dirname + pdfdir + file;
console.log(filepath);
html += "File: " + pdfdir + file + "\n";
});
res.write(html);
}
else {
res.write('Incorrect link');
}
res.end();
});
server.listen(port, hostname, () => {
console.log(`Server running at http://${hostname}:${port}/`);
});
Can somebody please guide me with the next steps?

// imports first
const
http = require('http'),
url = require('url'),
querystring = require('querystring'),
fs = require('fs'),
path = require('path');
const
hostname = '127.0.0.1',
port = 3000;
// main function should only route requests
const server = http.createServer((req, res) => {
const params = querystring.parse(url.parse(req.url).query);
if ('id' in params) {
handleDirectoryListing(req, res, params)
} else if ('file' in params) {
handleServeFile(req, res, params)
} else {
res.statusCode = 404
res.end('Incorrect link');
}
});
server.listen(port, hostname, () => {
console.log(`Server running at http://${hostname}:${port}/`);
});
function handleDirectoryListing (req, res, params) {
const pdfdir = `./files/${params.id}/`;
console.log(pdfdir);
// never use sync in an http handler
fs.readdir(pdfdir, function (err, files) {
if (err) {
res.statusCode = 404;
res.end('directory not found');
} else {
let html = `<!DOCTYPE html>
<meta charset="utf-8">
<title>Test page</title>
<h1>Test page</h1>
<div>id: ${params.id}</div>
`;
html += files.map(file => {
const filepath = path.join(pdfdir, file);
console.log(filepath);
return `<dir>File: ${pdfdir}${file}</dir>`;
}).join('\n');
res.end(html);
}
});
}
// serving files is easy, but note, this is not secure or efficient
function handleServeFile (req, res, params) {
const filepath = path.join(__dirname, params.file);
fs.createReadStream(filepath).pipe(res);
}
Though once you grasp it you should use a module such as express to make it clean, secure, efficient etc . . .

Related

Automatic exit - NodeJS https webserver

To elaborate on the question in the title,
I have made a simple app with js that runs on a node server. I have a thumbdrive that contains a folder and a start.bat file. Start.bat, as the name implies, switches the directory to my server folder and starts the server. Start.bat also starts another process that opens the edge browser to localhost in kiosk mode. When a user starts start.bat, the app will appear on the screen with the server running in the background. When the user exits the edge browser, they are then required to CTRL + C out of the server cmd prompt to properly shut down the server.
I need a system which effectively automatically shuts down the server after the Edge browser has been closed. I am not sure if it is possible to tie together the closing of the browser and the node server and am yet to find a solution online. If anyone has any ideas regarding possible fixes to my problem I would love to hear it!
https-server.js
const https = require("https");
const path = require("path");
const fs = require("fs");
const ip = require("ip");
const process = require("process");
const app = express();
const port = 443;
process.chdir("..");
console.log("Current working dir: " + process.cwd());
var rootDir = process.cwd();
//determines what folder houses js, css, html, etc files
app.use(express.static(rootDir + "/public/"), function (req, res, next) {
const ip = req.ip;
console.log("Now serving ip:", "\x1b[33m", ip, "\x1b[37m");
next();
});
//determines which file is the index
app.get("/", function (req, res) {
res.sendFile(path.join(rootDir + "/public/index.html"));
});
var sslServer = https.createServer(
{
key: fs.readFileSync(path.join(rootDir, "certificate", "key.pem")),
cert: fs.readFileSync(path.join(rootDir, "certificate", "certificate.pem")),
},
app
);
//determines which port app (http server) should listen on
sslServer.listen(port, function () {
console.log(
"Server has successfully started, available on:",
"\x1b[33m",
ip.address(),
"\x1b[37m",
"listening on port:",
"\x1b[33m",
+port,
"\x1b[37m"
);
console.log("CTRL + C to exit server");
sslServer.close();
});
Will provide any needed information.
Have an endpoint registered to exit the process
app.get('/shutdown', (req, res, next) => {
res.json({"message": "Received"});
next();
}, () => {
process.exit();
});
Then register a listener for onbeforeunload to do a request to this endpoint.
let terminateCmdReceived = false;
async function shutdown(e) {
let response;
if (!terminateCmdReceived) {
e.preventDefault();
try {
response = await fetch('http://localhost:3000/shutdown');
const json = await response.json();
if(json.message === "Received") {
terminateCmdReceived = true;
window.close();
}
} catch (e) {
console.error("Terminate Command was not received");
}
}
}
window.onbeforeunload = shutdown

File-upload error using Cordova FileTransfer to NodeJS

I'm currently working on a Phonegap app, and I would like users to be able to upload any file to my NodeJS server.
I've looking all around the web but I just can't get anything to work...
Here is the code I'm using for the Phonegap controller:
$scope.open = function()
{
navigator.camera.getPicture(upload,
function(message)
{
alert('get picture failed');
},
{
quality: 50,
destinationType: navigator.camera.PictureSourceType.FILE_URI,
sourceType: navigator.camera.PictureSourceType.PHOTOLIBRARY,
mediaType: navigator.camera.MediaType.ALLMEDIA
});
}
var win = function (r) {
$scope.log = "Code = " + r.responseCode;
$scope.log2 = "Response = " + r.response;
$scope.log3 = "Sent = " + r.bytesSent;
$scope.$digest();
}
var fail = function (error) {
$scope.log = "An error has occurred: Code = " + error.code;
$scope.log2 = "upload error source " + error.source;
$scope.log3 = "upload error target " + error.target;
$scope.$digest();
}
function upload(fileURI)
{
$scope.log = fileURI;
$scope.$digest();
var options = new FileUploadOptions();
options.fileKey = "file";
options.fileName = fileURI.substr(fileURI.lastIndexOf('/') + 1);
options.mimeType = "text/plain";
options.chunkedMode = false;
var params = {};
params.value1 = "test";
params.value2 = "param";
options.params = params;
var ft = new FileTransfer();
ft.upload(fileURI, "http://192.168.192.111:2999/upload", win, fail, options);
}.
Here is the current code for the NodeJS server, have tried a lot of different things, all without success:
var express = require('express');
var http = require('http').Server(express);
var io = require('socket.io')(http);
var fs = require('fs');
var multer = require('multer');
var app = new express();
app.post('/upload', multer({dest: './uploads/'}).single('upl'), function(req, res)
{
console.log(req.body);
console.log(req.file);
})
http.listen(2999, function(){
console.log('listening on *:2999');
});
In the app I used to get errors that FileUploadOptions etc weren't defined, but I fixed that by adding them to the cordova project.
Furthermore, I use ionic 1, if that helps anyone out.
I do keep constantly getting the error code 1 (upload error source), even though I selected a real file and I saw that the link was correct (something like /storage/0/emulated/Downloads on my Android device).
Also, sometimes it gives me error 3 as well (upload target source), some sort of server not found issue I think.
Is there something obvious I'm doing wrong and how would I be able to fix it? Is there a handier way, since I eventually want to link this to a MySQL database.
Thanks in advance!
Well found my answer (a while ago, this is for people stumbling across this post).
You can first try whether your JS works by changing the server to https://posttestserver.com/post.php. If you see an upload appearing there, there's a problem with the server.
The problem with me was that I didn't let Apache through the firewall at all, so uploads from anything besides my PC would fail...
var express=require('express');
var bodyParser=require('body-parser');
var formidable = require('formidable');
var util = require('util');
var app=express();
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());
var path=require('path');
var mysql =require('mysql');
var fs=require('fs');
app.use('/public',express.static(path.join(__dirname, 'public')));
var connection = mysql.createConnection({
host: 'localhost',
user: 'root',
password : '',
port : 3306, //port mysql
database:'xxxxx'
});
app.post('/data', function(req, res) {
// create an incoming form object
var form = new formidable.IncomingForm(),
files = [],
fields = [];
// specify that we want to allow the user to upload multiple files in a single request
form.multiples = true;
// store all uploads in the /uploads directory
form.uploadDir = path.join(__dirname, '/public/images/uploads');
// every time a file has been uploaded successfully,
// rename it to it's orignal name
form.on('file', function(field, file) {
if (path.extname(file.name)=='') {
extension='.jpg';
}
else{
extension=path.extname(file.name);
}
var oldpath = file.path;
form.uploadDir = path.basename(file.name,extension).concat((Math.random()* 100),extension);
var newpath = './public/images/uploads/'+ path.basename(file.name,extension).concat((Math.random()* 100),extension);
//fs.rename(file.path, path.join(form.uploadDir, file.name));
fs.rename(oldpath, newpath);
});
form.on('field', function(field, value) {
fields[field] = value;
});
// log any errors that occur
form.on('error', function(err) {
console.log('An error has occured: \n' + err);
});
// once all the files have been uploaded, send a response to the client
//Call back at the end of the form.
form.on('end', function () {
res.writeHead(200, {
'content-type': 'text/plain'
});
res.write('received the data:\n\n');
// console.log(fields.name+'-'+fields.nickname);
var values={
name:fields.name,
nickname:fields.nickname,
email:fields.email,
password:fields.password,
dob:fields.dob,
gender:fields.gender,
phoneno:fields.phone
};
connection.query('INSERT INTO users SET ?', values, function(err,req,res){
if(err){
console.log('Connection result error '+err);
}
else{
console.log('Success');
}
});
res.end();
});
// parse the incoming request containing the form data
form.parse(req);
});
//app.use(app.router);
app.listen(5000);

NodeJS write response to browser multiple times

I have a simple nodeJS server that fetches data from another server and store them in a JSON files, i need to write a status about each file fetched and generated, but that doesn't work, because i have to execute response.end(), which implies that i can't write to the stream again, without ending the stream
here's my code:
var http = require('http');
var module = require('fs');
var APIs = [ '/servlet/en', '/servlet/fr' ];
var langs =[ 'en', 'fr' ];
var finish = false;
var host = 'http://www.localtest';
const port = process.argv[2] || 9000;
var responses = [];
http.createServer(function (req, response) {
for (x in APIs){
console.log(x);
var options = {
host: 'localtest',
port: 8888,
path: APIs[x],
lang: langs[x]
};
http.get(options, function(res) {
res.setEncoding('utf8');
var body='';
res.on('data', function(chunk){
body += chunk;
});
res.on('end', function(chunk){
responses.push(body);
if (responses.length == 2){
var d = JSON.parse(responses[1]);
var d2 = JSON.parse(responses[0]);
module.writeFileSync("options.lang1"+".json",JSON.stringify(d) , 'utf-8');
module.writeFileSync("options.lang2"+".json",JSON.stringify(d2) , 'utf-8');
}
});
});
}
}).listen(parseInt(port));
console.log(`Server listening on port ${port}`);
An example, i tried to write a message to the user after the line :
responses.push(body);
using response.write(), but this method needs an response.end() in order to be executed and displayed on the browser, If i do that i can't write to the stream anymore!
Couple issues with your code here. First off, you shouldn't use module as a variable, as that is a word that's already used in node's moduling system, e.g. in module.exports
Second, You really want to have some control flow in there. here's a complete example using the async library, though others prefer Promises.
var http = require('http');
var fs = require('fs');
var APIs = [ '/servlet/en', '/servlet/fr' ];
var langs =[ 'en', 'fr' ];
var host = 'http://www.localtest';
const port = process.argv[2] || 9000;
const async = require('async');
let responses = [];
function fetchAndWriteFile(lang, callback){
var options = {
host: 'localtest',
port: 8888,
path: '/servlet/'+lang,
lang: lang
};
http.get(options, function(res) {
res.setEncoding('utf8');
const filename = 'options.'+lang+'.json';
const fileStream = fs.createWriteStream(filename, {defaultEncoding: 'utf-8'});
fileStream.on('end', (e)=> {
if(e) return callback(e);
return callback(null, filename);
});
res.pipe(fileStream)
});
}
http.createServer(function (req, response) {
// this will run the fetchAndWriteFile once for each lang in langs
async.map(langs, fetchAndWriteFile, (e, files) => {
response.end(files); // files will be an array of filenames saved
});
}).listen(parseInt(port));
console.log(`Server listening on port ${port}`);

Issues Streaming MP4s with Webtorrent

I'm running a Node Server that I want to stream videos from magnet links that uses WebTorrent(https://webtorrent.io/docs). When I run this, it appears as if the file is not being correctly referenced even though I have set a variable as the .mp4 file.
Just to be clear, I added in a given torrentID(magnet link) in this example to eliminate any problems I may have with express and the URLs. This magnet link leads to a download of a music video in MP4 format.
The video player is showing up, but no video is being played. I'm assuming this means that I am not trying to access the correct file. If you need to know more about WebTorrent to help me, you can read about it at https://webtorrent.io/docs
var fs = require("fs"),
http = require("http"),
url = require("url"),
path = require("path"),
request = require('request'),
host = '127.0.0.1',
port = 3000,
express = require("express"),
app = express(),
server = http.createServer(app),
WebTorrent = require('webtorrent'),
client = new WebTorrent();
app.get('/streamvid/:magLink', function(req, res){
//var torrentID = req.params.magLink;
var torrentID = 'magnet:?xt=urn:btih:84123E8B4E850A796403736E0CF02E409F0EF00B';
client.add(torrentID, function (torrent) {
var file = torrent.files[0]
file.name = 'movie.mp4';
if (req.url != "/movie.mp4") {
res.writeHead(200, { "Content-Type": "text/html" });
res.end('<video width="1024" height="768" controls> <source src="movie.mp4" type="video/mp4"> <source src="movie.ogg" type="video/ogg"> Your browser does not support the video tag. </video>');
} else {
var range = req.headers.range;
var positions = range.replace(/bytes=/, "").split("-");
var start = parseInt(positions[0], 10);
fs.stat(file, function(err, stats) {
var total = stats.size;
var end = positions[1] ? parseInt(positions[1], 10) : total - 1;
var chunksize = (end - start) + 1;
res.writeHead(206, {
"Content-Range": "bytes " + start + "-" + end + "/" + total,
"Accept-Ranges": "bytes",
"Content-Length": chunksize,
"Content-Type": "video/mp4"
});
var stream = fs.createReadStream(file, { start: start, end: end })
.on("open", function() {
stream.pipe(res);
}).on("error", function(err) {
res.end(err);
});
});
}
})
});
var server = http.createServer(app);
var server = app.listen(port, host);
server.on('error', function(err) {
console.log('error:' + err);
});
server.on('listening', function(){
console.log('Server is Up and Running');
});
You need to either pipe the file data by reading it.
var readFile = fs.createReadStream("path/to/movie.mp4");
readFile.pipe(res);
Or have the file in a public route. app.use(express.static('public')) and put movie.mp4 in the public/ folder. Then in your src, do a full url link. http://localhost:3000/movie.mp4.

Socket.io connection via IP address not working

I got a game working with socket.io. It's working fine when playing locally and via my IP address (not LAN but real IP) when connecting via my own computer.
However, when I give my IP and port to someone else, the index HTML page is loaded all fine but the socket.io 'connection' doesn't work.
It displays the error on line 1659 of socket.io.js.
Socket.prototype.handshake = function (fn) {
var self = this
, options = this.options;
function complete (data) {
if (data instanceof Error) {
self.connecting = false;
self.onError(data.message);
} else {
fn.apply(null, data.split(':'));
}
};
var url = [
'http' + (options.secure ? 's' : '') + ':/'
, options.host + ':' + options.port
, options.resource
, io.protocol
, io.util.query(this.options.query, 't=' + +new Date)
].join('/');
if (this.isXDomain() && !io.util.ua.hasCORS) {
var insertAt = document.getElementsByTagName('script')[0]
, script = document.createElement('script');
script.src = url + '&jsonp=' + io.j.length;
insertAt.parentNode.insertBefore(script, insertAt);
io.j.push(function (data) {
complete(data);
script.parentNode.removeChild(script);
});
} else {
var xhr = io.util.request();
xhr.open('GET', url, true);
if (this.isXDomain()) {
xhr.withCredentials = true;
}
xhr.onreadystatechange = function () {
if (xhr.readyState == 4) {
xhr.onreadystatechange = empty;
if (xhr.status == 200) {
complete(xhr.responseText);
} else if (xhr.status == 403) {
self.onError(xhr.responseText);
} else {
self.connecting = false;
!self.reconnecting && self.onError(xhr.responseText);
}
}
};
xhr.send(null); //This is the line 1659.
}
};
Note: All the files are inside a folder on C: drive, not under a User.
Is the problem related to security access? Or something else?
Code for Server + Client
//Server
express = require('express');
http = require('http');
app = express();
server = http.createServer(app);
io = require('socket.io').listen(server);
app.use(express.static(__dirname + '/public'));
server.listen(3000);
app.get('/', function (req, res) { res.sendfile(__dirname + '/index.html'); });
//Client
<script src="/socket.io/socket.io.js"></script>
<script>var socket = io.connect('http://192.168.1.161:3000');</script>
Router Configuration
http://puu.sh/3ACGz.png
Make sure that your port (for socket.io) is forwarded by your router. And you are using public IP (static).
As well you should remember that most browsers will not allow to connect via WebSockets to another address/port from the page. For security reasons, your IP/Domain and Port should be the same as your IP/Domain and Port you server html and js from.

Resources