
Welcome!
Everything is fine.
File Upload¶
File upload is an important feature of many web apps. Think about uploading profile pictures or posting pictures of your vacation or breakfast to Facebook, Instagram or whatever. Your project should find a place for it.
Plan¶
- Announcements
- Basics of file upload, both on front-end and back-end
- Quiz Questions
- Breakout sessions
Announcements¶
- Grading status
- CRUD partners assigned. Please start ASAP.
CRUD Partners¶
- 2 Ada+Maya
- 4 Alyssa+Lyra
- 6 Amy+Sarah
- 8 AnnaLieb+Nya
- 10 AnnaZhou+Catherine
- 12 Annie+Thea
- 14 Annissa+Austen
- 16 Arya+Lauren
- 18 Becky+Kathy
- 20 Bella+Kaitlyn
- 22 Bellen+Chelsea
- 24 Dechen+Mary Jo
- 26 Eleanor+Nico
- 28 Fridah+Maria
- 30 Jada+Jelimo
- 32 Sofia+Soo
File Upload Story¶
First, a story ....
Summary¶
What are the important points for today?
- File Upload is an important feature: images, documents, etc. Your projects should try to include this in some way.
- File Upload is fraught with peril — be conscious of security.
- New kind of form input and form encoding type (
ENCTYPE
). - Sometimes need to learn about MIME types, such as
image/jpeg
- Two major places to store them:
- The filesystem, meaning regular files; store pathnames in the database. This is often more convenient and efficient. We'll focus on this.
- The database (as encoded strings). Do this if you either cannot save to the filesystem or are extremely security conscious (maybe people are supposed to be uploading executable scripts, but you need to keep them safe). We'll punt this topic this semester, but let me know if you personally want to know more.
- Files are connected to entities by storing the
filename as an property in the database document, or having a
pattern like the filename is
<nm>.jpg
. Images in the database are easily connected. - Serving images from the filesystem is easy: either use the
staticServer
feature (for public files) or create a handler that determines the pathname and then doesres.sendFile(pathname)
- File upload forms have:
ENCTYPE="multipart/form-data"
<INPUT TYPE=FILE ...>
method=POST
- The back-end parses the request, gets the file, and saves it to the filesystem (or the database).
Detailed Review¶
Review of File Upload and how to use them in Express.
- Use
milter
middleware - configure it to specify storage (directory and filename), as well as limits
- the
req.file
value gets you a file object for each upload. - each file has properties like
.filename
res.sendFile()
lets you serve these files
File Upload Example¶
Here's the main code (open.js
) for today:
// start app with either 'npm run dev' or 'node server.js'
// go to http://149.130.15.5:3000/
// standard modules, loaded from node_modules
const path = require('path');
require("dotenv").config({ path: path.join(process.env.HOME, '.cs304env')});
const express = require('express');
const morgan = require('morgan');
const serveStatic = require('serve-static');
const bodyParser= require('body-parser');
const cookieSession = require('cookie-session');
const flash = require('express-flash');
const multer = require('multer');
// our modules loaded from cwd
const { Connection } = require('./connection');
const cs304 = require('./cs304');
// Create and configure the app
const app = express();
// Morgan reports the final status code of a request's response
app.use(morgan('tiny'));
app.use(cs304.logStartRequest);
// This handles POST data
app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.json());
app.use(cs304.logRequestData); // tell the user about any request data
app.use(serveStatic('public'));
app.set('view engine', 'ejs');
const mongoUri = cs304.getMongoUri();
app.use(cookieSession({
name: 'session',
keys: [cs304.randomString(20)],
// Cookie Options
maxAge: 24 * 60 * 60 * 1000 // 24 hours
}));
app.use(flash());
// ================================================================
// configure Multer
app.use('/uploads', express.static('uploads'));
function timeString(dateObj) {
if( !dateObj) {
dateObj = new Date();
}
d2 = (val) => val < 10 ? '0'+val : ''+val;
let hh = d2(dateObj.getHours())
let mm = d2(dateObj.getMinutes())
let ss = d2(dateObj.getSeconds())
return hh+mm+ss
}
const ALLOWED_EXTENSIONS = ['.jpg', '.jpeg', '.png', '.gif' ];
var storage = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, 'uploads')
},
filename: function (req, file, cb) {
// the path module provides a function that returns the extension
let ext = path.extname(file.originalname).toLowerCase();
console.log('extension', ext);
let hhmmss = timeString();
cb(null, file.fieldname + '-' + hhmmss + ext);
}
})
var upload = multer(
{ storage: storage,
// check whether the file should be allowed
// should also install and use mime-types
// https://www.npmjs.com/package/mime-types
fileFilter: function(req, file, cb) {
let ext = path.extname(file.originalname).toLowerCase();
let ok = ALLOWED_EXTENSIONS.includes(ext);
console.log('file ok', ok);
if(ok) {
cb(null, true);
} else {
cb(null, false, new Error('not an allowed extension:'+ext));
}
},
// max fileSize in bytes
limits: {fileSize: 1_000_000 }});
// ================================================================
// custom routes here
// collections in the user's personal database
const DB = process.env.USER;
const FILES = 'files';
app.get('/', async (req, res) => {
const db = await Connection.open(mongoUri, DB);
let files = await db.collection(FILES).find({}).toArray();
return res.render('open.ejs', {uploads: files});
});
app.post('/upload', upload.single('photo'), async (req, res) => {
console.log('uploaded data', req.body);
console.log('file', req.file);
// insert file data into mongodb
const db = await Connection.open(mongoUri, DB);
const result = await db.collection(FILES)
.insertOne({title: req.body.title,
path: '/uploads/'+req.file.filename});
console.log('insertOne result', result);
// always nice to confirm with the user
req.flash('info', 'file uploaded');
return res.redirect('/');
});
app.post('/clear', async (req, res) => {
const db = await Connection.open(mongoUri, DB);
const result = await db.collection(FILES).deleteMany({});
return res.redirect('/');
});
// ================================================================
// postlude
const serverPort = cs304.getPort(8080);
// this is last, because it never returns
app.listen(serverPort, function() {
console.log(`File documents will be stored in user's database: ${DB}`);
console.log(`open http://localhost:${serverPort}`);
});
Here's a link to the fileUpload folder.
Quiz Questions¶
- 0
- 5
- 14
- 10
- 0
I'll answer your quiz questions
Optional Demo¶
There's a file upload video in the videos collection. But I will do a live demo today.
cd ~/cs304/apps
cp -rd ~cs304node/apps/fileUpload fileUpload
cd fileUpload
Here are the steps of the demo:
- Find a couple of pictures to use for the demo. Put them someplace handy, such as your Desktop
- Run the app:
node private.js
- Register as one username and upload a picture
- Click on the
/myphotos
link, too. - Logout
- Register as another username and upload a picture
- Try to view the first person's pictures
Starting CRUD¶
Let's all do the getting started steps together.
Breakouts¶
- You can work on Lookup, CRUD, or your project draft
Summary¶
- Provide file upload if it's useful and relevant
- Worry about security, but don't panic