MIME - Multipurpose Internet Mail Extensions, an Internet standard that describes the type of message content.
http://www.w3school.com.cn/media/media_mimeref.asp
The "content type" we set in the code before is "text/plain" and "text/html". Two different MIME settings.
We often use "application/javascript" (javascript), "image/png"(png), "image/jpeg".
The browser can parse the passed data according to the content type.
In the previous code, we directly specified that the content type of res is a string. Let's unify MIME in a module, and then introduce it where necessary.
We create a new file mime.js under src/helper, define the MIME type in mime.js, and then export a method to return the MIME type according to the extension. As follows.
const path = require('path'); const mimeType = { 'css': 'text/css', 'gif': 'image/gif', 'html': 'text/html', 'ico': 'image/x-icon', 'jpeg': 'imag/jpeg', 'jpg': 'image/jpeg', 'json': 'application/json', 'js': 'text/javascript', 'pdf': 'application/pdf', 'png': 'image/png', 'svg': 'image/svg+xml', 'swf': 'application/x-shockwave-flash', 'tiff': 'image/tiff', 'txt': 'text/plain', 'wav': 'audio/x-wav', 'wma': 'audio/x-ms-wma', 'wmv': 'video/x-ms-wmv', 'xml': 'text/xml' } module.exports = (filePath) => { let ext = path.extname(filePath).split('.').pop().toLowerCase(); if(!ext) { ext = filePath; } return mimeType[ext] || mimeType['txt']; }
OK, then, let's go where we use MIME and try it. route.js under src/helper. As follows.
const fs = require('fs'); const path = require('path'); const ejs = require('ejs'); const promisify = require('util').promisify; const conf = require('../config/defaultConfig'); const mime = require('./mime'); const stat = promisify(fs.stat); const readdir = promisify(fs.readdir); const ejsPath = path.join(__dirname, '../templates/main-page.ejs'); const source = fs.readFileSync(ejsPath,'utf-8'); module.exports = async function (req, res, filePath) { try { const stats = await stat(filePath); if (stats.isFile()) { const contentType = mime(filePath); res.statusCode = 200; res.setHeader('Content-Type', contentType); fs.createReadStream(filePath).pipe(res) } if (stats.isDirectory()) { const files = await readdir(filePath); res.statusCode = 200; res.setHeader('Content-Type', 'text/html'); const dir = path.relative(conf.root, filePath) const data = { title: path.basename(filePath), dir: dir ? `/${dir}` : '', files }; res.end(ejs.render(source, data)); } } catch(ex) { res.statusCode = 404; res.setHeader('Content-Type', 'text/plain'); res.end(`${filePath} is not a file or directory`); } }
For beauty, we can add an icon for each file. This can be written in route.js.
const fs = require('fs'); const path = require('path'); const ejs = require('ejs'); const promisify = require('util').promisify; const conf = require('../config/defaultConfig'); const mime = require('./mime'); const stat = promisify(fs.stat); const readdir = promisify(fs.readdir); const ejsPath = path.join(__dirname, '../templates/main-page.ejs'); const source = fs.readFileSync(ejsPath,'utf-8'); module.exports = async function (req, res, filePath) { try { const stats = await stat(filePath); if (stats.isFile()) { const contentType = mime(filePath); res.statusCode = 200; res.setHeader('Content-Type', contentType); fs.createReadStream(filePath).pipe(res) } if (stats.isDirectory()) { const files = await readdir(filePath); res.statusCode = 200; res.setHeader('Content-Type', 'text/html'); const dir = path.relative(conf.root, filePath) const data = { title: path.basename(filePath), dir: dir ? `/${dir}` : '', files: files.map((file) => { return { file, icon: mime(file) } }) }; res.end(ejs.render(source, data)); } } catch(ex) { res.statusCode = 404; res.setHeader('Content-Type', 'text/plain'); res.end(`${filePath} is not a file or directory`); } }
Then, let's change the template, as follows.
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Document</title> <style> body { margin: 30px; } a { display: block; font-size: 15px; } </style> </head> <body> <% files.map( file => { %> <a href="<%= dir %>/<%= file %>"> [<%= file.icon %>] <%= file.file %> </a> <% }) %> </body> </html>
Of course, if you can add a small icon according to the icon, it will not be demonstrated.