how to serve a directory with express? - javascript

I would like to create a simple express server that sends a directory like the image following:
Browser directory picture
const express = require('express');
const path = require('path');
const app = express();
app.use(express.static(path.join(__dirname, 'shaders')));
app.use('*', (req, res) => {
res.sendFile((path.join(__dirname, 'shaders')));
});
const PORT = 3000;
app.listen(PORT, () => {
console.log('listening on port ', PORT);
});
This code displays Cannot GET / in the browser window.

Using a library
There are libraries that already do this for you, for example serve-index.
Doing it yourself
This is a modified version of your code to show file content or list the files/directories in a directory. I've added some comments to explain what's happening, but feel free to ask more questions if something is not clear.
const express = require("express");
const path = require("path");
const fs = require("fs");
const app = express();
const listingPath = path.join(__dirname, "shaders");
app.get("*", (req, res) => {
// Build the path of the file using the URL pathname of the request.
const filePath = path.join(listingPath, req.path);
// If the path does not exist, return a 404.
if (!fs.existsSync(filePath)) {
return res.status(404).end();
}
// Check if the existing item is a directory or a file.
if (fs.statSync(filePath).isDirectory()) {
const filesInDir = fs.readdirSync(filePath);
// If the item is a directory: show all the items inside that directory.
return res.send(filesInDir);
} else {
const fileContent = fs.readFileSync(filePath, 'utf8');
// If the item is a file: show the content of that file.
return res.send(fileContent);
}
});
const PORT = 3000;
app.listen(PORT, () => {
console.log("listening on port ", PORT);
});
You can use this as a base to make a template that includes links to the files/directories, to include a link to the parent directory, to show more meta data ...

You can use a static folder for sharing or fetch files via GET request.
app.use(express.static(path.join(__dirname, 'shaders')));

This code displays Cannot GET / in the browser window.
Sending a GET to / will fallback to your app.use * as you don't have a route defined. It's not clear what this should do as you're returning a directory instead of a file, which isn't going to work.
If you'd like to access a specific file, you need to request it directly as localhost:3000/shaders/xxx, etc. The use of express.static appears to be correct.

Related

Image Successfully Downloads From Node Server But Does Not Display

I have a folder on my node server called /pictures/renamed. In it, I'm trying to save an image and it does successfully save it however it doesn't display anything except a message that says "It appears that we don't support this file format". I changed the image extension to jpeg, jpg and png but the image still doesnt display. Why is this happening?
const express = require('express');
const bodyParser = require('body-parser')
const app = express();
const https = require('https');
const fs = require('fs');
const cors = require('cors');
const pathh = require('path')
//Setting up the cors config
app.use(cors());
//BodyParser middleware
app.use(express.json());
function download(){
const url = 'https://unsplash.com/photos/3aqiY4t1qxM/download?ixid=MnwxMjA3fDB8MXxhbGx8Mnx8fHx8fDJ8fDE2NTY3Njg2Mzc&force=true'
const path = pathh.resolve("./pictures/renamed/", 'imageWorld.jpg')
const fileStream = fs.createWriteStream(path)
https.get(url,(res)=>{
res.pipe(fileStream)
fileStream.on('finish',()=>{
fileStream.close()
})
})
}
download();
//Serve static files if in production
if(process.env.NODE_ENV === 'production'){
//Set static folder
app.use(express.static('client/build'));
app.get('*', (req, res) =>{
res.sendFile(path.resolve(__dirname, 'client', 'build', 'index.html'))
});
}
const port = process.env.PORT || 5000;
app.listen(port, () => console.log(`Server started on port ${port}`));
That's because the response is an html document (telling you that you are redirected) rather than actual jpg data. It looks something like this in a text editor:
<html><body>You are being redirected.</body></html>So, what you saved was not a valid photo file.
Referring to https://github.com/follow-redirects/follow-redirects, I ran npm install follow-redirects then replaced const https = require('https') with const { https } = require('follow-redirects') in the js file, and then I could get the jpg.

Image not displayed in browser after being uploaded with node

I am trying to upload some pics using a node-express server and I am facing two issues, one is regarding the picture extension and the other one regarding the picture itself being displayed.
So after I am uploading the file is added into the upload path but a kind like this auto-generated name 2f22c2502b907f7bf0bc2567c43c801cwithout extension like .png and even if I'm renaming the file like 2f22c2502b907f7bf0bc2567c43c801c.png in browser is looking like default empty icon browser instead of uploaded picture.
How can I solve those two issues ?
const express = require('express')
const multer = require('multer')
const path = require('path')
const child_process = require("child_process");
const app = express();
const PORT = 3000;
const upload = multer({ dest: 'uploads/' })
app.post('/upload', upload.single('picture'), function (req, res, next) {
const FILE_OUTPUT = path.join(__dirname, `uploads/${req.file.path}`)
child_process.execFile(
"/usr/bin/convert",
[path.join(__dirname, req.file.path), "-resize", "280x150", FILE_OUTPUT],
function() {
console.log('done resizing', FILE_OUTPUT)
return res.send(`
<img src="${FILE_OUTPUT}"/>
`);
}
);
});
app.use('/', express.static(path.join(__dirname, 'public')));
app.listen(PORT, function () { console.log('Example app listening on port: ', PORT) })
That should be a multer task because multer will not append the file extension so what need to be done is a bit of setup on multer.
Check this response multer setup

Express.static is not serving login page

I set the homepage to login.html, but why does the homepage be index.html?
I tried to delete index.html. It turns out that the web shows login.html so I was wondering.
app.js
const express = require('express')
const bodyParser = require('body-parser');
const path = require('path');
const app = express()
const PORT = process.env.PORT || 3000
express.static('public')
app.use(express.static('public'))
app.use(bodyParser.urlencoded({extended : true}));
app.use(bodyParser.json());
app.get('/', function (req, res) {
res.sendFile(path.join(__dirname,'/public/login.html'))
})
app.listen(PORT, () => {
console.log(`Server is running on port : ${PORT}`)
})
public folder
public
---> css
---> img
index.html
login.html
In regards to serve-static, the documentation says:
By default this module will send “index.html” files in response to a request on a directory. To disable this set false or to supply a new index pass a string or an array in preferred order.
http://expressjs.com/en/resources/middleware/serve-static.html
In your example, you can disable this default behavior by setting the "index" option to "false":
app.use(express.static("public", { index: false }));
There is also an option to pass an array (as mentioned in the documentation):
app.use(express.static("public", { 'index': ['login.html', 'index.htm'] }));

String interpolation on static files served by Nodejs

Hi I have Nodejs server which serves static resource at /assets/meta-info.json which contains:
{
"environmentName": "${ENV_NAME}"
}
The problem is: How to replace ${ENV_NAME} with corresponding system environment variable?
You can probably save it as /assets/meta-info.js
You can import 'dotenv' npm library.
In assets/meta-info.js
require('dotenv').config();
module.exports = {
"environmentName": process.env.ENV_NAME
}
Have a .env file (no extension). have line below:
ENV_NAME=prod
You could modify the file when the server starts and request that modified file (or you can rename the original file and keep serve original modified file)
something like modifying the original file during runtime (which is not advisable as you will modifying the same file again and again) =>
const fs = require('fs');
const path = require('path');
const express = require('express');
const app = express();
app.get('/', (req, res) => {
let fileBuffer = fs.readFileSync('./manifest.json', 'utf-8');
fileBuffer = fileBuffer.replace('${ENV_NAME}', process.env.NODE_ENV);
fs.writeFileSync('./temp.json', fileBuffer);
res.sendFile(path.join(__dirname, './temp.json'));
});
app.listen(4000, () => {
console.log('listening and working');
});
Instead modify it once and send the modified copy.
let fileBuffer = fs.readFileSync('./manifest.json', 'utf-8');
fileBuffer = fileBuffer.replace('${ENV_NAME}', process.env.NODE_ENV);
fs.writeFileSync('./temp.json', fileBuffer);
const app = express();
app.get('/', (req, res) => {
res.sendFile(path.join(__dirname, './temp.json'));
});
Now if you are using something like express.serveStatic, then I would create a backup copy of file and modify the original file in place.

Structure event listeners in Node.js [duplicate]

This question already has answers here:
How to separate routes on Node.js and Express 4?
(9 answers)
Closed 1 year ago.
In my NodeJS express application I have app.js that has a few common routes. Then in a wf.js file I would like to define a few more routes.
How can I get app.js to recognize other route handlers defined in wf.js file?
A simple require does not seem to work.
If you want to put the routes in a separate file, for example routes.js, you can create the routes.js file in this way:
module.exports = function(app){
app.get('/login', function(req, res){
res.render('login', {
title: 'Express Login'
});
});
//other routes..
}
And then you can require it from app.js passing the app object in this way:
require('./routes')(app);
Have a look at these examples: https://github.com/visionmedia/express/tree/master/examples/route-separation
In Express 4.x you can get an instance of the router object and import another file that contains more routes. You can even do this recursively so your routes import other routes allowing you to create easy-to-maintain URL paths.
For example, if I have a separate route file for my /tests endpoint already and want to add a new set of routes for /tests/automated I may want to break these /automated routes out into a another file to keep my /test file small and easy to manage. It also lets you logically group routes together by URL path which can be really convenient.
Contents of ./app.js:
var express = require('express'),
app = express();
var testRoutes = require('./routes/tests');
// Import my test routes into the path '/test'
app.use('/tests', testRoutes);
Contents of ./routes/tests.js:
var express = require('express'),
router = express.Router();
var automatedRoutes = require('./testRoutes/automated');
router
// Add a binding to handle '/tests'
.get('/', function(){
// render the /tests view
})
// Import my automated routes into the path '/tests/automated'
// This works because we're already within the '/tests' route
// so we're simply appending more routes to the '/tests' endpoint
.use('/automated', automatedRoutes);
module.exports = router;
Contents of ./routes/testRoutes/automated.js:
var express = require('express'),
router = express.Router();
router
// Add a binding for '/tests/automated/'
.get('/', function(){
// render the /tests/automated view
})
module.exports = router;
Building on #ShadowCloud 's example I was able to dynamically include all routes in a sub directory.
routes/index.js
var fs = require('fs');
module.exports = function(app){
fs.readdirSync(__dirname).forEach(function(file) {
if (file == "index.js") return;
var name = file.substr(0, file.indexOf('.'));
require('./' + name)(app);
});
}
Then placing route files in the routes directory like so:
routes/test1.js
module.exports = function(app){
app.get('/test1/', function(req, res){
//...
});
//other routes..
}
Repeating that for as many times as I needed and then finally in app.js placing
require('./routes')(app);
If you're using express-4.x with TypeScript and ES6, this would be the best template to use:
src/api/login.ts
import express, { Router, Request, Response } from "express";
const router: Router = express.Router();
// POST /user/signin
router.post('/signin', async (req: Request, res: Response) => {
try {
res.send('OK');
} catch (e) {
res.status(500).send(e.toString());
}
});
export default router;
src/app.ts
import express, { Request, Response } from "express";
import compression from "compression"; // compresses requests
import expressValidator from "express-validator";
import bodyParser from "body-parser";
import login from './api/login';
const app = express();
app.use(compression());
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
app.use(expressValidator());
app.get('/public/hc', (req: Request, res: Response) => {
res.send('OK');
});
app.use('/user', login);
app.listen(8080, () => {
console.log("Press CTRL-C to stop\n");
});
Much cleaner than using var and module.exports.
Full recursive routing of all .js files inside /routes folder, put this in app.js.
// Initialize ALL routes including subfolders
var fs = require('fs');
var path = require('path');
function recursiveRoutes(folderName) {
fs.readdirSync(folderName).forEach(function(file) {
var fullName = path.join(folderName, file);
var stat = fs.lstatSync(fullName);
if (stat.isDirectory()) {
recursiveRoutes(fullName);
} else if (file.toLowerCase().indexOf('.js')) {
require('./' + fullName)(app);
console.log("require('" + fullName + "')");
}
});
}
recursiveRoutes('routes'); // Initialize it
in /routes you put whatevername.js and initialize your routes like this:
module.exports = function(app) {
app.get('/', function(req, res) {
res.render('index', { title: 'index' });
});
app.get('/contactus', function(req, res) {
res.render('contactus', { title: 'contactus' });
});
}
And build yet more on the previous answer, this version of routes/index.js will ignore any files not ending in .js (and itself)
var fs = require('fs');
module.exports = function(app) {
fs.readdirSync(__dirname).forEach(function(file) {
if (file === "index.js" || file.substr(file.lastIndexOf('.') + 1) !== 'js')
return;
var name = file.substr(0, file.indexOf('.'));
require('./' + name)(app);
});
}
I am trying to update this answer with "express": "^4.16.3". This answer is similar to the one from ShortRound1911.
server.js:
const express = require('express');
const mongoose = require('mongoose');
const bodyParser = require('body-parser');
const db = require('./src/config/db');
const routes = require('./src/routes');
const port = 3001;
const app = new express();
//...use body-parser
app.use(bodyParser.urlencoded({ extended: true }));
//...fire connection
mongoose.connect(db.url, (err, database) => {
if (err) return console.log(err);
//...fire the routes
app.use('/', routes);
app.listen(port, () => {
console.log('we are live on ' + port);
});
});
/src/routes/index.js:
const express = require('express');
const app = express();
const siswaRoute = require('./siswa_route');
app.get('/', (req, res) => {
res.json({item: 'Welcome ini separated page...'});
})
.use('/siswa', siswaRoute);
module.exports = app;
/src/routes/siswa_route.js:
const express = require('express');
const app = express();
app.get('/', (req, res) => {
res.json({item: 'Siswa page...'});
});
module.exports = app;
If you want a separate .js file to better organize your routes, just create a variable in the app.js file pointing to its location in the filesystem:
var wf = require(./routes/wf);
then,
app.get('/wf', wf.foo );
where .foo is some function declared in your wf.js file. e.g
// wf.js file
exports.foo = function(req,res){
console.log(` request object is ${req}, response object is ${res} `);
}
One tweak to all of these answers:
var routes = fs.readdirSync('routes')
.filter(function(v){
return (/.js$/).test(v);
});
Just use a regex to filter via testing each file in the array. It is not recursive, but it will filter out folders that don't end in .js
I know this is an old question, but I was trying to figure out something like for myself and this is the place I ended up on, so I wanted to put my solution to a similar problem in case someone else has the same issues I'm having. There's a nice node module out there called consign that does a lot of the file system stuff that is seen here for you (ie - no readdirSync stuff). For example:
I have a restful API application I'm trying to build and I want to put all of the requests that go to '/api/*' to be authenticated and I want to store all of my routes that go in api into their own directory (let's just call it 'api'). In the main part of the app:
app.use('/api', [authenticationMiddlewareFunction], require('./routes/api'));
Inside of the routes directory, I have a directory called "api" and a file called api.js. In api.js, I simply have:
var express = require('express');
var router = express.Router();
var consign = require('consign');
// get all routes inside the api directory and attach them to the api router
// all of these routes should be behind authorization
consign({cwd: 'routes'})
.include('api')
.into(router);
module.exports = router;
Everything worked as expected. Hope this helps someone.
index.js
const express = require("express");
const app = express();
const http = require('http');
const server = http.createServer(app).listen(3000);
const router = (global.router = (express.Router()));
app.use('/books', require('./routes/books'))
app.use('/users', require('./routes/users'))
app.use(router);
routes/users.js
const router = global.router
router.get('/', (req, res) => {
res.jsonp({name: 'John Smith'})
}
module.exports = router
routes/books.js
const router = global.router
router.get('/', (req, res) => {
res.jsonp({name: 'Dreams from My Father by Barack Obama'})
}
module.exports = router
if you have your server running local (http://localhost:3000) then
// Users
curl --request GET 'localhost:3000/users' => {name: 'John Smith'}
// Books
curl --request GET 'localhost:3000/books' => {name: 'Dreams from My Father by Barack Obama'}
I wrote a small plugin for doing this! got sick of writing the same code over and over.
https://www.npmjs.com/package/js-file-req
Hope it helps.
you can put all route functions in other files(modules) , and link it to the main server file.
in the main express file, add a function that will link the module to the server:
function link_routes(app, route_collection){
route_collection['get'].forEach(route => app.get(route.path, route.func));
route_collection['post'].forEach(route => app.post(route.path, route.func));
route_collection['delete'].forEach(route => app.delete(route.path, route.func));
route_collection['put'].forEach(route => app.put(route.path, route.func));
}
and call that function for each route model:
link_routes(app, require('./login.js'))
in the module files(for example - login.js file), define the functions as usual:
const login_screen = (req, res) => {
res.sendFile(`${__dirname}/pages/login.html`);
};
const forgot_password = (req, res) => {
console.log('we will reset the password here')
}
and export it with the request method as a key and the value is an array of objects, each with path and function keys.
module.exports = {
get: [{path:'/',func:login_screen}, {...} ],
post: [{path:'/login:forgotPassword', func:forgot_password}]
};

Categories

Resources