문제

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.

도움이 되었습니까?

해결책

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);
});

다른 팁

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!');
  });
});

express-fileupload

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.

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:

  • You check if req.busboy is set (the middleware was loaded correctly)
  • You set up a "file" listener on req.busboy
  • You pipe the contents of 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).

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.

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)
  });


});

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);
  });
}

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('/')

    });
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")
        } );                

    } );

});

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

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();
        });
    });
}
라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top