Tải lên hình ảnh bằng Node.js, Express và Mongoose


102

Vui lòng xem xét các câu trả lời mới hơn có nhiều thông tin cập nhật hơn vì mọi thứ đã thay đổi trong những năm qua!

Vì nhiều thư viện Node.js mới đang nhanh chóng bị lỗi thời và có rất ít ví dụ mà tôi muốn hỏi về việc tải lên hình ảnh bằng cách sử dụng:

  • Node.js (v0.4.1)
  • Express (1.0.7)
  • Mongoose (1.1.0).

Những người khác đã làm điều đó như thế nào?

Tôi đã tìm thấy: nút thật ghê gớm , nhưng tôi chưa quen với việc tải lên hình ảnh nói chung nên tôi muốn tìm hiểu những thứ chung và cách thực hiện bằng cách sử dụng Node.js và Express.


12
Cập nhật các phiên bản mới hơn của Express đã chức năng này được xây dựng trong, cho rằng trước khi dành thời gian với 'kết nối dạng'
user531694

4
2015 tl; dr- gửi yêu cầu nhiều phần / biểu mẫu đến máy chủ của bạn và phân tích cú pháp chúng bằng Multer vì BodyParser không còn phân tích cú pháp tệp nữa. npm install multer --savevà sau đó trong ứng dụng của bạn, bạn có thể truy cập req.files.your_file_param_namevà lưu vào s3 với aws-sdkhoặcfs.writeFile(...)
người dùng

Câu trả lời:


74

Tôi sẽ trả lời câu hỏi của riêng tôi lần đầu tiên. Tôi đã tìm thấy một ví dụ trực tiếp từ nguồn. Xin hãy tha thứ cho vết lõm kém. Tôi không chắc chắn cách thụt lề đúng cách khi sao chép và dán. Mã lấy ngay từ multipart/form-dataví dụ Express trên GitHub.

// Expose modules in ./support for demo purposes
require.paths.unshift(__dirname + '/../../support');

/**
 * Module dependencies.
 */

var express = require('../../lib/express')
  , form = require('connect-form');

var app = express.createServer(
  // connect-form (http://github.com/visionmedia/connect-form)
  // middleware uses the formidable middleware to parse urlencoded
  // and multipart form data
  form({ keepExtensions: true })
);

app.get('/', function(req, res){
  res.send('<form method="post" enctype="multipart/form-data">'
    + '<p>Image: <input type="file" name="image" /></p>'
    + '<p><input type="submit" value="Upload" /></p>'
    + '</form>');
});

app.post('/', function(req, res, next){

  // connect-form adds the req.form object
  // we can (optionally) define onComplete, passing
  // the exception (if any) fields parsed, and files parsed
  req.form.complete(function(err, fields, files){
    if (err) {
      next(err);
    } else {
      console.log('\nuploaded %s to %s'
        ,  files.image.filename
        , files.image.path);
      res.redirect('back');
    }
  });

  // We can add listeners for several form
  // events such as "progress"
  req.form.on('progress', function(bytesReceived, bytesExpected){
    var percent = (bytesReceived / bytesExpected * 100) | 0;
    process.stdout.write('Uploading: %' + percent + '\r');
  });
});

app.listen(3000);
console.log('Express app started on port 3000');

3
Vâng, nhưng làm thế nào để bạn lưu tệp?
Nick Retallack

1
@NickRetallack file lưu được lưu trữ tại files.image.path
Robin Duckett

@ robin-duckett, bạn đã chỉ định trước tên tệp và đường dẫn như thế nào?
Luc

4
@Luc: Bạn không, nó được lưu vào một thư mục tạm thời để bạn chuyển nó đi nơi khác.
kevmo314

1
Đây là cách bạn định cấu hình thư mục tải lên trong express: // LƯU Ý: sử dụng một đường dẫn tuyệt đối đến thư mục tải lên để tránh các sự cố trong mô-đun con! // app.use (express.bodyParser ({uploadDir: uploadDir}));
Risadinha

47

Vì bạn đang sử dụng express, chỉ cần thêm bodyParser:

app.use(express.bodyParser());

thì tuyến đường của bạn tự động có quyền truy cập vào (các) tệp đã tải lên trong req.files:

app.post('/todo/create', function (req, res) {
    // TODO: move and rename the file using req.files.path & .name)
    res.send(console.dir(req.files));  // DEBUG: display available fields
});

Nếu bạn đặt tên cho điều khiển đầu vào là "todo" như thế này (trong Jade):

form(action="/todo/create", method="POST", enctype="multipart/form-data")
    input(type='file', name='todo')
    button(type='submit') New

Sau đó, tệp tải lên đã sẵn sàng vào thời điểm bạn nhận được đường dẫn và tên tệp gốc trong 'files.todo':

  • req.files.todo.path và
  • req.files.todo.name

các thuộc tính req.files hữu ích khác:

  • kích thước (tính bằng byte)
  • gõ (ví dụ: 'image / png')
  • lastModifiedate
  • _writeStream.encoding (ví dụ: 'nhị phân')

Tôi chưa bao giờ nghe nó giới thiệu theo cách đó và tôi sẽ tiếp tục và nói rằng những người này hiểu rõ nhất về developer.mozilla.org/en-US/docs/JavaScript/Guide/… vì vậy tôi đoán cả hai chúng tôi đều sai;)
srquinn

bodyParserlà không an toàn, ít nhất là theo điều này: andrewkelley.me/post/do-not-use-bodyparser-with-express-js.html . Câu trả lời của Jon J phù hợp với tôi.
Matt Browne

Bởi "không an toàn", nó chỉ có nghĩa là các tệp tạm thời được tạo ra, để một "cuộc tấn công" có thể lấp đầy không gian đĩa của máy chủ bằng các tệp tạm thời. Đây là một vấn đề nghiêm trọng hơn, vì nó không phải là một lỗ hổng bảo mật.
Brent Faust

19

Bạn có thể định cấu hình phần mềm trung gian phân tích cú pháp cơ thể kết nối trong khối cấu hình trong tệp ứng dụng chính của mình:

    /** Form Handling */
    app.use(express.bodyParser({
        uploadDir: '/tmp/uploads',
        keepExtensions: true
    }))
    app.use(express.limit('5mb'));

Đây thực sự là cách tốt nhất để xử lý tải lên mà tôi cho là. Giữ lại tệp nếu bạn muốn, chỉ cần xóa nó đi thay vì sao chép nó vào một vị trí riêng biệt. Cảm ơn.
Eastern Monk,

2
@AksharPrabhuDesai Có và không. Giả sử bạn có một công cụ tải lên / cắt ảnh. Nếu bạn cho phép người dùng tải trực tiếp lên thư mục chung của mình, bạn có một lỗ hổng bảo mật nghiêm trọng. Trong trường hợp này, cách tốt nhất là tải lên thư mục tmp và sau đó chuyển vào thư mục chung của bạn sau khi xác nhận tệp không phải là Trojan.
srquinn 24/09/13

Dường như không còn được hỗ trợ nữa. Có vẻ như một giải pháp tốt đẹp.
Blaze

14

Thấy chưa, điều tốt nhất bạn có thể làm là chỉ cần tải hình ảnh lên đĩa và lưu URL trong MongoDB. Nghỉ ngơi khi bạn lấy lại hình ảnh. Chỉ cần chỉ định URL và bạn sẽ nhận được một hình ảnh. Mã để tải lên như sau.

app.post('/upload', function(req, res) {
    // Get the temporary location of the file
    var tmp_path = req.files.thumbnail.path;
    // Set where the file should actually exists - in this case it is in the "images" directory.
    target_path = '/tmp/' + req.files.thumbnail.name;
    // Move the file from the temporary location to the intended location
    fs.rename(tmp_path, target_path, function(err) {
        if (err)
            throw err;
        // Delete the temporary file, so that the explicitly set temporary upload dir does not get filled with unwanted files.
        fs.unlink(tmp_path, function() {
            if (err)
                throw err;
            //
        });
    });
});

Bây giờ hãy lưu đường dẫn đích trong cơ sở dữ liệu MongoDB của bạn.

Một lần nữa, trong khi truy xuất hình ảnh, chỉ cần trích xuất URL từ cơ sở dữ liệu MongoDB và sử dụng nó trên phương pháp này.

fs.readFile(target_path, "binary", function(error, file) {
    if(error) {
        res.writeHead(500, {"Content-Type": "text/plain"});
        res.write(error + "\n");
        res.end();
    }
    else {
        res.writeHead(200, {"Content-Type": "image/png"});
        res.write(file, "binary");
    }
});

9

Hãy thử mã này, nó sẽ hữu ích.

app.get('/photos/new', function(req, res){
  res.send('<form method="post" enctype="multipart/form-data">'
    + '<p>Data: <input type="filename" name="filename" /></p>'
    + '<p>file: <input type="file" name="file" /></p>'
    + '<p><input type="submit" value="Upload" /></p>'
    + '</form>');
});


 app.post('/photos/new', function(req, res) {
  req.form.complete(function(err, fields, files) {
    if(err) {
      next(err);
    } else {
      ins = fs.createReadStream(files.photo.path);
      ous = fs.createWriteStream(__dirname + '/directory were u want to store image/' + files.photo.filename);
      util.pump(ins, ous, function(err) {
        if(err) {
          next(err);
        } else {
          res.redirect('/photos');
        }
      });
      //console.log('\nUploaded %s to %s', files.photo.filename, files.photo.path);
      //res.send('Uploaded ' + files.photo.filename + ' to ' + files.photo.path);
    }
  });
});

if (!module.parent) {
  app.listen(8000);
  console.log("Express server listening on port %d, log on to http://127.0.0.1:8000", app.address().port);
}

util.pump(ins, ous)được giảm giá, điều này có thể được thực hiện với ins.pipe(ous);bây giờ. Nhưng liệu điều này có xóa tập tin ảnh về vị trí cũ không?
Emiel Vandenbussche

8

Bạn cũng có thể sử dụng phần sau để đặt đường dẫn nơi nó lưu tệp.

req.form.uploadDir = "<path>";


2

Một lần nữa nếu bạn không muốn sử dụng bodyParser, các thao tác sau sẽ hoạt động:

var express = require('express');
var http = require('http');
var app = express();

app.use(express.static('./public'));


app.configure(function(){
    app.use(express.methodOverride());
    app.use(express.multipart({
        uploadDir: './uploads',
        keepExtensions: true
    }));
});


app.use(app.router);

app.get('/upload', function(req, res){
    // Render page with upload form
    res.render('upload');
});

app.post('/upload', function(req, res){
    // Returns json of uploaded file
    res.json(req.files);
});

http.createServer(app).listen(3000, function() {
    console.log('App started');
});

2

Đối với Express 3.0, nếu bạn muốn sử dụng các sự kiện lớn, bạn phải loại bỏ phần mềm trung gian nhiều phần, để bạn có thể tạo phiên bản mới của nó.

Để làm điều này:

app.use(express.bodyParser());

Có thể được viết như:

app.use(express.json());
app.use(express.urlencoded());
app.use(express.multipart()); // Remove this line

Và bây giờ hãy tạo đối tượng biểu mẫu:

exports.upload = function(req, res) {
    var form = new formidable.IncomingForm;
    form.keepExtensions = true;
    form.uploadDir = 'tmp/';

    form.parse(req, function(err, fields, files){
        if (err) return res.end('You found error');
        // Do something with files.image etc
        console.log(files.image);
    });

    form.on('progress', function(bytesReceived, bytesExpected) {
        console.log(bytesReceived + ' ' + bytesExpected);
    });

    form.on('error', function(err) {
        res.writeHead(400, {'content-type': 'text/plain'}); // 400: Bad Request
        res.end('error:\n\n'+util.inspect(err));
    });
    res.end('Done');
    return;
};

Tôi cũng đã đăng điều này trên blog của mình, Tải lên đối tượng biểu mẫu đáng gờm trong Express 3.0 .


Đề xuất của bạn là sai, bodyParser về cơ bản phân tích cú pháp biểu mẫu. và chấp nhận các biến cấu hình đáng gờm.
Marius

1
@timoxley đây chỉ là ví dụ
Risto Novik

1

Tôi biết rằng câu hỏi ban đầu liên quan đến các phiên bản cụ thể, nhưng câu hỏi này cũng đề cập đến "mới nhất" - bài đăng của @JohnAllen không còn phù hợp do Expressjs bodyParser và connect-form

Điều này cho thấy bodyParser () tích hợp sẵn dễ sử dụng:

 /**
 * Module dependencies.
 */

var express = require('express')

var app = express()
app.use(express.bodyParser({ keepExtensions: true, uploadDir: '/home/svn/rest-api/uploaded' }))

app.get('/', function(req, res){
  res.send('<form method="post" enctype="multipart/form-data">'
    + '<p>Image: <input type="file" name="image" /></p>'
    + '<p><input type="submit" value="Upload" /></p>'
    + '</form>');
});

app.post('/', function(req, res, next){

    res.send('Uploaded: ' + req.files.image.name)
    return next()

});

app.listen(3000);
console.log('Express app started on port 3000');

0

Có phương pháp của tôi để tải lên nhiều tệp:

Nodejs:

router.post('/upload', function(req , res) {

var multiparty = require('multiparty');
var form = new multiparty.Form();
var fs = require('fs');

form.parse(req, function(err, fields, files) {  
    var imgArray = files.imatges;


    for (var i = 0; i < imgArray.length; i++) {
        var newPath = './public/uploads/'+fields.imgName+'/';
        var singleImg = imgArray[i];
        newPath+= singleImg.originalFilename;
        readAndWriteFile(singleImg, newPath);           
    }
    res.send("File uploaded to: " + newPath);

});

function readAndWriteFile(singleImg, newPath) {

        fs.readFile(singleImg.path , function(err,data) {
            fs.writeFile(newPath,data, function(err) {
                if (err) console.log('ERRRRRR!! :'+err);
                console.log('Fitxer: '+singleImg.originalFilename +' - '+ newPath);
            })
        })
}
})

Đảm bảo rằng biểu mẫu của bạn có enctype = "multiart / form-data"

Tôi hy vọng điều này giúp bạn một tay;)


0

Đây là một cách để tải lên hình ảnh của bạn bằng cách sử dụng gói đáng kể, được khuyến nghị sử dụng qua bodyParser trong các phiên bản Express sau này. Điều này cũng bao gồm khả năng thay đổi kích thước hình ảnh của bạn một cách nhanh chóng:

Từ trang web của tôi: Tải lên và Thay đổi kích thước Hình ảnh (nhanh chóng) Với Node.js và Express .

Đây là ý chính:

var express = require("express"),
app = express(),
formidable = require('formidable'),
util = require('util')
fs   = require('fs-extra'),
qt   = require('quickthumb');

// Use quickthumb
app.use(qt.static(__dirname + '/'));

app.post('/upload', function (req, res){
  var form = new formidable.IncomingForm();
  form.parse(req, function(err, fields, files) {
    res.writeHead(200, {'content-type': 'text/plain'});
    res.write('received upload:\n\n');
    res.end(util.inspect({fields: fields, files: files}));
  });

  form.on('end', function(fields, files) {
    /* Temporary location of our uploaded file */
    var temp_path = this.openedFiles[0].path;
    /* The file name of the uploaded file */
    var file_name = this.openedFiles[0].name;
    /* Location where we want to copy the uploaded file */
    var new_location = 'uploads/';

    fs.copy(temp_path, new_location + file_name, function(err) {  
      if (err) {
        console.error(err);
      } else {
        console.log("success!")
      }
    });
  });
});

// Show the upload form 
app.get('/', function (req, res){
  res.writeHead(200, {'Content-Type': 'text/html' });
  /* Display the file upload form. */
  form = '<form action="/upload" enctype="multipart/form-data" method="post">'+ '<input name="title" type="text" />
  '+ '<input multiple="multiple" name="upload" type="file" />
  '+ '<input type="submit" value="Upload" />'+ '</form>';
  res.end(form); 
}); 
app.listen(8080);

LƯU Ý: Điều này yêu cầu Image Magick để thay đổi kích thước ngón tay cái nhanh chóng.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.