การอัปโหลดภาพโดยใช้ Node.js, Express และ Mongoose


102

โปรดพิจารณาคำตอบที่ใหม่กว่าซึ่งมีข้อมูลที่ทันสมัยกว่าเนื่องจากมีการเปลี่ยนแปลงในช่วงหลายปีที่ผ่านมา!

เนื่องจากไลบรารี Node.js ใหม่จำนวนมากถูกแสดงอย่างรวดเร็วล้าสมัยและมีตัวอย่างค่อนข้างน้อยฉันต้องการถามเกี่ยวกับการอัปโหลดภาพโดยใช้:

  • โหนด js (v0.4.1)
  • ด่วน (1.0.7)
  • พังพอน (1.1.0).

คนอื่นทำได้อย่างไร

ฉันพบว่าโหนดน่ากลัวแต่ฉันยังใหม่กับการอัปโหลดภาพโดยทั่วไปดังนั้นฉันจึงต้องการเรียนรู้สิ่งต่างๆทั่วไปและวิธีการทำเช่นนั้นโดยใช้ Node.js และ Express


12
อัปเดต Express เวอร์ชันที่ใหม่กว่ามีฟังก์ชันนี้ในตัวโปรดพิจารณาก่อนที่จะใช้เวลากับ "connect-form"
user531694

4
2015 tl; dr- ส่งคำขอหลายส่วน / แบบฟอร์มไปยังเซิร์ฟเวอร์ของคุณและแยกวิเคราะห์ด้วย Multer เนื่องจาก BodyParser ไม่แยกวิเคราะห์ไฟล์อีกต่อไป npm install multer --saveจากนั้นในแอปของคุณคุณสามารถเข้าถึงreq.files.your_file_param_nameและบันทึกเป็น s3 ด้วยaws-sdkหรือfs.writeFile(...)
ผู้ใช้

คำตอบ:


74

ฉันจะตอบคำถามของตัวเองเป็นครั้งแรก ฉันพบตัวอย่างตรงจากแหล่งที่มา โปรดอภัยเยื้องผู้น่าสงสาร ฉันไม่แน่ใจว่าจะเยื้องอย่างไรเมื่อคัดลอกและวางอย่างถูกต้อง รหัสมาจากmultipart/form-dataตัวอย่าง Express บน 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
ใช่ แต่คุณจะบันทึกไฟล์ได้อย่างไร?
Nick Retallack

1
@NickRetallack ไฟล์ที่บันทึกไว้จะถูกเก็บไว้ที่ files.image.path
Robin Duckett

@ robin-duckett คุณระบุชื่อไฟล์และพา ธ ไว้ก่อนได้อย่างไร?
ลัค

4
@Luc: คุณไม่ได้บันทึกลงในไดเร็กทอรีชั่วคราวซึ่งคุณจะย้ายไปที่อื่น
kevmo314

1
นี่คือวิธีที่คุณกำหนดค่าไดเร็กทอรีอัปโหลดในแบบด่วน: // หมายเหตุ: ใช้เส้นทางแบบสัมบูรณ์ไปยังไดเร็กทอรีอัปโหลดเพื่อหลีกเลี่ยงปัญหาในโมดูลย่อย! // app.use (express.bodyParser ({uploadDir: uploadDir}));
Risadinha

47

เนื่องจากคุณใช้ Express ให้เพิ่ม bodyParser:

app.use(express.bodyParser());

จากนั้นเส้นทางของคุณจะเข้าถึงไฟล์ที่อัปโหลดโดยอัตโนมัติใน 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
});

หากคุณตั้งชื่อตัวควบคุมการป้อนข้อมูลว่า "สิ่งที่ต้องทำ" เช่นนี้ (ใน Jade):

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

จากนั้นไฟล์ที่อัปโหลดจะพร้อมเมื่อคุณได้รับเส้นทางและชื่อไฟล์ต้นฉบับใน 'files.todo':

  • req.files.todo.path และ
  • req.files.todo.name

คุณสมบัติ req.files ที่มีประโยชน์อื่น ๆ :

  • ขนาด (เป็นไบต์)
  • ประเภท (เช่น 'image / png')
  • lastModifiedate
  • _writeStream.encoding (เช่น 'ไบนารี')

ฉันไม่เคยได้ยินว่ามันอ้างถึงแบบนั้นและฉันจะบอกต่อไปว่าคนเหล่านี้รู้จักนักพัฒนาที่ดีที่สุด. mozilla.org/en-US/docs/JavaScript/Guide/…ดังนั้นฉันเดาว่าเราผิดทั้งคู่)
srquinn

bodyParserไม่ปลอดภัยอย่างน้อยตามไปนี้: andrewkelley.me/post/do-not-use-bodyparser-with-express-js.html คำตอบของจอนเจใช้ได้ผลกับฉัน
Matt Browne

โดย "ไม่ปลอดภัย" หมายความว่าไฟล์ชั่วคราวถูกสร้างขึ้นดังนั้น "การโจมตี" อาจทำให้พื้นที่ดิสก์ของเซิร์ฟเวอร์เต็มไปด้วยไฟล์ชั่วคราว นี่เป็นปัญหาด้านความแข็งแรงมากกว่าเนื่องจากไม่ใช่ช่องโหว่ด้านความปลอดภัย
Brent Faust

19

คุณสามารถกำหนดค่ามิดเดิลแวร์ตัวแยกวิเคราะห์ตัวเชื่อมต่อในบล็อกการกำหนดค่าในไฟล์แอปพลิเคชันหลักของคุณ:

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

นี่เป็นวิธีที่ดีที่สุดในการจัดการการอัปโหลดที่ฉันคิดว่า เก็บไฟล์ไว้หากคุณต้องการเพียงแค่ลบออกแทนที่จะคัดลอกไปยังตำแหน่งอื่น ขอบคุณ.
พระภาคตะวันออก

2
@AksharPrabhuDesai ใช่และไม่ใช่ สมมติว่าคุณมีเครื่องมืออัปโหลด / ครอบตัดรูปภาพ หากคุณอนุญาตให้ผู้ใช้อัปโหลดโดยตรงไปยังโฟลเดอร์สาธารณะของคุณแสดงว่าคุณมีข้อบกพร่องด้านความปลอดภัยอย่างร้ายแรง ในกรณีนี้ทางที่ดีที่สุดคืออัปโหลดไปยังโฟลเดอร์ tmp จากนั้นย้ายไปยังโฟลเดอร์สาธารณะของคุณหลังจากยืนยันว่าไฟล์ไม่ใช่โทรจัน
srquinn

ดูเหมือนจะไม่ได้รับการสนับสนุนอีกต่อไป ดูเหมือนเป็นทางออกที่ดี
Blaze

14

ดูสิ่งที่ดีที่สุดที่คุณทำได้คืออัปโหลดภาพไปยังดิสก์และบันทึก URL ใน MongoDB พักเมื่อคุณดึงภาพอีกครั้ง เพียงระบุ URL คุณก็จะได้ภาพ รหัสสำหรับการอัพโหลดมีดังนี้

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

ตอนนี้บันทึกเส้นทางเป้าหมายในฐานข้อมูล MongoDB ของคุณ

อีกครั้งในขณะที่ดึงภาพให้ดึง URL จากฐานข้อมูล MongoDB และใช้วิธีนี้

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

ลองใช้รหัสนี้มันจะช่วยได้

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)เป็นค่าเสื่อมราคาซึ่งสามารถทำได้ในins.pipe(ous);ตอนนี้ แต่จะลบไฟล์ภาพในตำแหน่งเก่าหรือไม่
Emiel Vandenbussche

8

คุณยังสามารถใช้สิ่งต่อไปนี้เพื่อกำหนดเส้นทางที่จะบันทึกไฟล์

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

5

ฉันสร้างตัวอย่างที่ใช้ Express และ Multer มันง่ายมากและหลีกเลี่ยงคำเตือนConnectทั้งหมด

มันอาจช่วยใครสักคน


1
ขอบคุณสำหรับสิ่งนี้. เป็นเรื่องยากที่จะหาตัวอย่างที่สมบูรณ์และทันสมัยที่ไม่ได้ใช้bodyParser(ซึ่งไม่ปลอดภัยโปรดดูandrewkelley.me/post/do-not-use-bodyparser-with-express-js.html )
Matt Browne

2

อีกครั้งหากคุณไม่ต้องการใช้ bodyParser การทำงานต่อไปนี้:

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

สำหรับ Express 3.0 หากคุณต้องการใช้เหตุการณ์ที่น่ากลัวคุณต้องลบมิดเดิลแวร์หลายพาร์ทเพื่อให้คุณสร้างอินสแตนซ์ใหม่ได้

เพื่อทำสิ่งนี้:

app.use(express.bodyParser());

สามารถเขียนเป็น:

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

และตอนนี้สร้างวัตถุฟอร์ม:

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

ฉันยังได้โพสต์นี้บนบล็อกของฉันเดินทางวัตถุรูปแบบที่น่ากลัว in Express 3.0 ในการอัปโหลด


คำแนะนำของคุณทำให้พลาดไม่ได้โดยทั่วไป bodyParser จะแยกวิเคราะห์แบบฟอร์ม และยอมรับตัวแปรกำหนดค่าที่น่ากลัว
Marius

1
@timoxley นี่เป็นเพียงตัวอย่าง
Risto Novik

1

ฉันรู้ว่าคำถามเดิมที่เกี่ยวข้องกับเวอร์ชันเฉพาะ แต่ยังอ้างถึง "ล่าสุด" - โพสต์ของ @JohnAllen ไม่เกี่ยวข้องอีกต่อไปเนื่องจากExpressjs bodyParser และ connect-form

สิ่งนี้แสดงให้เห็นถึง bodyParser ในตัวที่ใช้งานง่าย ():

 /**
 * 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

มีวิธีการของฉันในการอัปโหลดไฟล์หลายไฟล์:

โหนด:

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

ตรวจสอบว่าฟอร์มของคุณมี enctype = "multipart / form-data"

ฉันหวังว่านี่จะช่วยให้คุณได้รับ;)


0

ต่อไปนี้เป็นวิธีอัปโหลดภาพของคุณโดยใช้แพ็คเกจที่น่าเกรงขามซึ่งแนะนำให้ใช้กับ bodyParser ใน Express เวอร์ชันที่ใหม่กว่า นอกจากนี้ยังรวมถึงความสามารถในการปรับขนาดภาพของคุณได้ทันที:

จากเว็บไซต์ของฉัน: อัปโหลดและการปรับขนาดภาพ (ได้ทันที) ด้วย Node.js และเอ็กซ์เพรส

นี่คือสาระสำคัญ:

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

หมายเหตุ: ต้องใช้ Image Magick สำหรับการปรับขนาดหัวแม่มืออย่างรวดเร็ว

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.