Reputation: 1527
I'm using node v0.10.26 and express v4.2.0 and I'm pretty new to node. I've been beating my head against my desk for the past three or so hours trying to get a file upload form working with node. At this point I'm just trying to get req.files to not return undefined. My view looks like this
<!DOCTYPE html>
<html>
<head>
<title>{{ title }}</title>
<link rel='stylesheet' href='/stylesheets/style.css' />
</head>
<body>
<h1>{{ title }}</h1>
<p>Welcome to {{ title }}</p>
<form method='post' action='upload' enctype="multipart/form-data">
<input type='file' name='fileUploaded'>
<input type='submit'>
</form>
</body>
</html>
Here are my routes
var express = require('express');
var router = express.Router();
/* GET home page. */
router.get('/', function(req, res) {
res.render('index', { title: 'Express' });
});
router.post('/upload', function(req, res){
console.log(req.files);
});
module.exports = router;
And here's my app.js
var express = require('express');
var path = require('path');
var favicon = require('static-favicon');
var logger = require('morgan');
var cookieParser = require('cookie-parser');
var bodyParser = require('body-parser');
var routes = require('./routes/index');
var users = require('./routes/users');
var app = express();
// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'hjs');
app.use(favicon());
app.use(logger('dev'));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded());
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));
app.use('/', routes);
app.use('/users', users);
/// catch 404 and forward to error handler
app.use(function(req, res, next) {
var err = new Error('Not Found');
err.status = 404;
next(err);
});
/// error handlers
// development error handler
// will print stacktrace
if (app.get('env') === 'development') {
app.use(function(err, req, res, next) {
res.status(err.status || 500);
res.render('error', {
message: err.message,
error: err
});
});
}
// production error handler
// no stacktraces leaked to user
app.use(function(err, req, res, next) {
res.status(err.status || 500);
res.render('error', {
message: err.message,
error: {}
});
});
module.exports = app;
I saw somewhere that including methodOverride()
and bodyParser({keepExtensions:true,uploadDir:path})
was supposed to help but I can't even launch my server if I add those lines.
Upvotes: 128
Views: 330588
Reputation: 1
const File = require("../models/file");
const path = require('path');
exports.localFileUpload = async (req, res) => {
try {
let sampleFile = req.files.sampleFile;
console.log("File uploaded:", sampleFile);
let uploadPath = path.join(__dirname, "/files", Date.now() + "_" + sampleFile.name);
console.log("Upload path:", uploadPath);
sampleFile.mv(uploadPath, function(err) {
if (err) {
console.log(err);
return res.status(500).json({
success: false,
});
}
});
return res.json({
success: true,
message: "File uploaded successfully"
});
} catch (error) {
console.error(error);
res.status(500).send({
success: false,
message: 'Server Error',
});
}
}
__dirname
, a directory /files
, and a unique filename with the current timestamp and original filename.Upvotes: 0
Reputation: 2871
Another option is to use multer, which uses busboy under the hood, but is simpler to set up.
var multer = require('multer');
var upload = multer({dest: './uploads/'});
You can register a global middleware and set the destination for uploads:
app.use(upload.single('avatar'));
Then in any POST where you expect a file, you can access the data:
app.post('/', function(req, res) {
// req.file (singular) is the file
console.dir(req.file);
});
Rather than a globally registered middleware, you may want to just set it on specific routes:
app.post('/', upload.single('avatar'), function(req, res){
console.dir(req.file);
});
Other middleware options include fields
, array
, and none
(for text-only multipart). E.g. Sending multiple files:
app.post('/', uploads.array('photos', 12), function(req, res){
// req.files (plural) is an array of files
console.dir(req.files);
});
When creating a form in your view, enctype='multipart/form-data
is required for multer to work:
form(role="form", action="/", method="post", enctype="multipart/form-data")
div(class="form-group")
label Upload File
input(type="file", name="avatar", id="avatar")
For more examples check out the github page.
Upvotes: 35
Reputation: 182
In vanila node, I worked something out. Just a quick upload server. Not the fancy express stuff. Probably not what the questioner wants.
const fs = require('fs'),
http = require('http'),
port = process.env.PORT || 9000,
host = process.env.HOST || '127.0.0.1';
//tested on node=v10.19.0, export HOST="192.168.0.103"
http.createServer(function(req, res) {
// Check if form is submitted and save its content
if (req.method == "POST") try {
store_file(req)
// This is here incase any errors occur
} catch (err) {
res.writeHead(404);
res.end('Server Borked');
return;
}
// respond with a simple html form so they can post more data
res.writeHead(200, {"content-type":"text/html; charset=utf-8"});
res.end(`
<form method="post" enctype="multipart/form-data">
<input type="file" name="fileUpload">
<input type="submit" value="Upload">
</form>`);
}).listen(port, host, () => console.dir(`Serving at http://${host}:${port}`));
function store_file(req) {
// Generate temporary file name
var temp = 'temp' + Math.floor(Math.random() * 10);
// This opens up the writeable stream to temporary file
var writeStream = fs.createWriteStream(temp);
// This pipes the POST data to the file
req.pipe(writeStream);
// After the temporary file is creates, create real file
writeStream.on('finish', () => {
reader = fs.readFileSync(temp);
filename = reader.slice(reader.indexOf("filename=\"") + "filename=\"".length, reader.indexOf('"\r\nContent-Type'));
hash = reader.slice(0,reader.indexOf('\r\n'));
content = reader.slice(reader.indexOf('\r\n\r\n') + '\r\n\r\n'.length, reader.lastIndexOf(Buffer.from('\r\n') + hash));
// After real file is created, delete temporary file
fs.writeFileSync(filename.toString(), content);
fs.unlinkSync(temp);
});
}
Upvotes: 2
Reputation: 3358
const http = require('http');
const fs = require('fs');
// https://www.npmjs.com/package/formidable
const formidable = require('formidable');
// https://stackoverflow.com/questions/31317007/get-full-file-path-in-node-js
const path = require('path');
router.post('/upload', (req, res) => {
console.log(req.files);
let oldpath = req.files.fileUploaded.path;
// https://stackoverflow.com/questions/31317007/get-full-file-path-in-node-js
let newpath = path.resolve( `./${req.files.fileUploaded.name}` );
// copy
// https://stackoverflow.com/questions/43206198/what-does-the-exdev-cross-device-link-not-permitted-error-mean
fs.copyFile( oldpath, newpath, (err) => {
if (err) throw err;
// delete
fs.unlink( oldpath, (err) => {
if (err) throw err;
console.log('Success uploaded")
} );
} );
});
Upvotes: 1
Reputation: 610
I find this, simple and efficient:
const express = require('express');
const fileUpload = require('express-fileupload');
const app = express();
// default options
app.use(fileUpload());
app.post('/upload', function(req, res) {
if (!req.files || Object.keys(req.files).length === 0) {
return res.status(400).send('No files were uploaded.');
}
// The name of the input field (i.e. "sampleFile") is used to retrieve the uploaded file
let sampleFile = req.files.sampleFile;
// Use the mv() method to place the file somewhere on your server
sampleFile.mv('/somewhere/on/your/server/filename.jpg', function(err) {
if (err)
return res.status(500).send(err);
res.send('File uploaded!');
});
});
Upvotes: 50
Reputation: 9054
If you are using Node.js Express and Typescript here is a working example, this works with javascript also, just change the let to var and the import to includes etc...
first import the following make sure you install formidable by running the following command:
npm install formidable
than import the following:
import * as formidable from 'formidable';
import * as fs from 'fs';
then your function like bellow:
uploadFile(req, res) {
let form = new formidable.IncomingForm();
form.parse(req, function (err, fields, files) {
let oldpath = files.file.path;
let newpath = 'C:/test/' + files.file.name;
fs.rename(oldpath, newpath, function (err) {
if (err) throw err;
res.write('File uploaded and moved!');
res.end();
});
});
}
Upvotes: 0
Reputation: 859
Personally multer didn't work for me after weeks trying to get this file upload thing right. Then I switch to formidable and after a few days I got it working perfectly without any error, multiple files, express and react.js even though react is optional. Here's the guide: https://www.youtube.com/watch?v=jtCfvuMRsxE&t=122s
Upvotes: 0
Reputation: 151
Here is an easier way that worked for me:
const express = require('express');
var app = express();
var fs = require('fs');
app.post('/upload', async function(req, res) {
var file = JSON.parse(JSON.stringify(req.files))
var file_name = file.file.name
//if you want just the buffer format you can use it
var buffer = new Buffer.from(file.file.data.data)
//uncomment await if you want to do stuff after the file is created
/*await*/
fs.writeFile(file_name, buffer, async(err) => {
console.log("Successfully Written to File.");
// do what you want with the file it is in (__dirname + "/" + file_name)
console.log("end : " + new Date())
console.log(result_stt + "")
fs.unlink(__dirname + "/" + file_name, () => {})
res.send(result_stt)
});
});
Upvotes: 7
Reputation: 3075
Multer is a node.js middleware for handling multipart/form-data, which is primarily used for uploading files. It is written on top of busboy for maximum efficiency.
npm install --save multer
in app.js
var multer = require('multer');
var storage = multer.diskStorage({
destination: function (req, file, callback) {
callback(null, './public/uploads');
},
filename: function (req, file, callback) {
console.log(file);
callback(null, Date.now()+'-'+file.originalname)
}
});
var upload = multer({storage: storage}).single('photo');
router.route("/storedata").post(function(req, res, next){
upload(req, res, function(err) {
if(err) {
console.log('Error Occured');
return;
}
var userDetail = new mongoOp.User({
'name':req.body.name,
'email':req.body.email,
'mobile':req.body.mobile,
'address':req.body.address
});
console.log(req.file);
res.end('Your File Uploaded');
console.log('Photo Uploaded');
userDetail.save(function(err,result){
if (err) {
return console.log(err)
}
console.log('saved to database')
})
})
res.redirect('/')
});
Upvotes: 1
Reputation: 18549
I needed to be walked through with a bit more detail than the other answers provided (e.g. how do I write the file to a location I decide at runtime?). Hopefully this is of help to others:
get connect-busboy:
npm install connect-busboy --save
In your server.js, add these lines
let busboy = require('connect-busboy')
// ...
app.use(busboy());
// ...
app.post('/upload', function(req, res) {
req.pipe(req.busboy);
req.busboy.on('file', function(fieldname, file, filename) {
var fstream = fs.createWriteStream('./images/' + filename);
file.pipe(fstream);
fstream.on('close', function () {
res.send('upload succeeded!');
});
});
});
This would seem to omit error handling though... will edit it in if I find it.
Upvotes: 5
Reputation: 3000
Here is a simplified version (the gist) of Mick Cullen's answer -- in part to prove that it needn't be very complex to implement this; in part to give a quick reference for anyone who isn't interested in reading pages and pages of code.
You have to make you app use connect-busboy:
var busboy = require("connect-busboy");
app.use(busboy());
This will not do anything until you trigger it. Within the call that handles uploading, do the following:
app.post("/upload", function(req, res) {
if(req.busboy) {
req.busboy.on("file", function(fieldName, fileStream, fileName, encoding, mimeType) {
//Handle file stream here
});
return req.pipe(req.busboy);
}
//Something went wrong -- busboy was not loaded
});
Let's break this down:
req.busboy
is set (the middleware was loaded correctly)"file"
listener on req.busboy
req
to req.busboy
Inside the file listener there are a couple of interesting things, but what really matters is the fileStream
: this is a Readable, that can then be written to a file, like you usually would.
Pitfall: You must handle this Readable, or express will never respond to the request, see the busboy API (file section).
Upvotes: 22
Reputation: 9464
ExpressJS Issue:
Most of the middleware is removed from express 4. check out: http://www.github.com/senchalabs/connect#middleware For multipart middleware like busboy, busboy-connect, formidable, flow, parted is needed.
This example works using connect-busboy middleware.
create /img and /public folders.
Use the folder structure:
\server.js
\img\"where stuff is uploaded to"
\public\index.html
SERVER.JS
var express = require('express'); //Express Web Server
var busboy = require('connect-busboy'); //middleware for form/file upload
var path = require('path'); //used for file path
var fs = require('fs-extra'); //File System - for file manipulation
var app = express();
app.use(busboy());
app.use(express.static(path.join(__dirname, 'public')));
/* ==========================================================
Create a Route (/upload) to handle the Form submission
(handle POST requests to /upload)
Express v4 Route definition
============================================================ */
app.route('/upload')
.post(function (req, res, next) {
var fstream;
req.pipe(req.busboy);
req.busboy.on('file', function (fieldname, file, filename) {
console.log("Uploading: " + filename);
//Path where image will be uploaded
fstream = fs.createWriteStream(__dirname + '/img/' + filename);
file.pipe(fstream);
fstream.on('close', function () {
console.log("Upload Finished of " + filename);
res.redirect('back'); //where to go next
});
});
});
var server = app.listen(3030, function() {
console.log('Listening on port %d', server.address().port);
});
INDEX.HTML
<!DOCTYPE html>
<html lang="en" ng-app="APP">
<head>
<meta charset="UTF-8">
<title>angular file upload</title>
</head>
<body>
<form method='post' action='upload' enctype="multipart/form-data">
<input type='file' name='fileUploaded'>
<input type='submit'>
</body>
</html>
The following will work with formidable SERVER.JS
var express = require('express'); //Express Web Server
var bodyParser = require('body-parser'); //connects bodyParsing middleware
var formidable = require('formidable');
var path = require('path'); //used for file path
var fs =require('fs-extra'); //File System-needed for renaming file etc
var app = express();
app.use(express.static(path.join(__dirname, 'public')));
/* ==========================================================
bodyParser() required to allow Express to see the uploaded files
============================================================ */
app.use(bodyParser({defer: true}));
app.route('/upload')
.post(function (req, res, next) {
var form = new formidable.IncomingForm();
//Formidable uploads to operating systems tmp dir by default
form.uploadDir = "./img"; //set upload directory
form.keepExtensions = true; //keep file extension
form.parse(req, function(err, fields, files) {
res.writeHead(200, {'content-type': 'text/plain'});
res.write('received upload:\n\n');
console.log("form.bytesReceived");
//TESTING
console.log("file size: "+JSON.stringify(files.fileUploaded.size));
console.log("file path: "+JSON.stringify(files.fileUploaded.path));
console.log("file name: "+JSON.stringify(files.fileUploaded.name));
console.log("file type: "+JSON.stringify(files.fileUploaded.type));
console.log("astModifiedDate: "+JSON.stringify(files.fileUploaded.lastModifiedDate));
//Formidable changes the name of the uploaded file
//Rename the file to its original name
fs.rename(files.fileUploaded.path, './img/'+files.fileUploaded.name, function(err) {
if (err)
throw err;
console.log('renamed complete');
});
res.end();
});
});
var server = app.listen(3030, function() {
console.log('Listening on port %d', server.address().port);
});
Upvotes: 102