nodejs-使用multer實現多張圖片上傳,express搭建腳手架
在工作中,我們經常會看到用戶有多張圖片上傳,並且預覽展示的需求.那么在具體實現中又該怎么做呢?
本實例需要nodejs基礎,本實例盡可能的在代碼中實現了解讀,如有疑惑點,歡迎提問。但是提問之前,希望你能先自行嘗試解決,鍛煉解決問題的能力。
首先使用express腳手架生成,並且安裝依賴
express --ejs
npm i
在routes文件夾中建立/api/v1/img.js,在app.js中寫入代碼引入img.js,安裝img.js依賴
app.use('/api/v1/img',require('./routes/api/v1/img'));
npm i multer moment
HTML代碼

<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width,initial-scale=1"> <title>圖片上傳</title> <link rel="stylesheet" href="stylesheets/tinyImgUpload.css"> </head> <body> <div id="upload"> </div> <button class="submit">submit</button> <script src="javascripts/tinyImgUpload.js"></script> <script> document.documentElement.style.fontSize = document.documentElement.clientWidth*0.1+'px'; var options = { path: 'api/v1/img/file_upload', onSuccess: function (res) { console.log(res); }, onFailure: function (res) { console.log(res); } } var upload = tinyImgUpload('#upload', options); document.getElementsByClassName('submit')[0].onclick = function (e) { upload(); } </script> </body> </html>
JS代碼

/** * tinyImgUpload * @param ele [string] [生成組件的元素的選擇器] * @param options [Object] [對組件設置的基本參數] * options具體參數如下 * path 圖片上傳的地址路徑 必需 * onSuccess(res) 文件上傳成功后的回調 參數為返回的文本 必需 * onFailure(res) 文件上傳失敗后的回調 參數為返回的文本 必需 * @return [function] [執行圖片上傳的函數] * 調用方法 * tinyImgUpload('div', options) */ function tinyImgUpload(ele, options) { // 判斷容器元素合理性並且添加基礎元素 var eleList = document.querySelectorAll(ele); if(eleList.length == 0){ console.log('綁定的元素不存在'); return; }else if(eleList.length>1){ console.log('請綁定唯一元素'); return; }else { eleList[0].innerHTML ='<div id="img-container" >'+ '<div class="img-up-add img-item"> <span class="img-add-icon">+</span> </div>'+ '<input type="file" name="files" id="img-file-input" multiple>'+ '</div>'; var ele = eleList[0].querySelector('#img-container'); ele.files = []; // 當前上傳的文件數組 } // 為添加按鈕綁定點擊事件,設置選擇圖片的功能 var addBtn = document.querySelector('.img-up-add'); addBtn.addEventListener('click',function () { document.querySelector('#img-file-input').value = null; document.querySelector('#img-file-input').click(); return false; },false) // 預覽圖片 //處理input選擇的圖片 function handleFileSelect(evt) { var files = evt.target.files; for(var i=0, f; f=files[i];i++){ // 過濾掉非圖片類型文件 if(!f.type.match('image.*')){ continue; } // 過濾掉重復上傳的圖片 var tip = false; for(var j=0; j<(ele.files).length; j++){ if((ele.files)[j].name == f.name){ tip = true; break; } } if(!tip){ // 圖片文件綁定到容器元素上 ele.files.push(f); var reader = new FileReader(); reader.onload = (function (theFile) { return function (e) { var oDiv = document.createElement('div'); oDiv.className = 'img-thumb img-item'; // 向圖片容器里添加元素 oDiv.innerHTML = '<img class="thumb-icon" src="'+e.target.result+'" />'+ '<a href="javscript:;" class="img-remove">x</a>' ele.insertBefore(oDiv, addBtn); }; })(f); reader.readAsDataURL(f); } } } document.querySelector('#img-file-input').addEventListener('change', handleFileSelect, false); // 刪除圖片 function removeImg(evt) { if(evt.target.className.match(/img-remove/)){ console.log('3',ele.files); // 獲取刪除的節點的索引 function getIndex(ele){ if(ele && ele.nodeType && ele.nodeType == 1) { var oParent = ele.parentNode; var oChilds = oParent.children; for(var i = 0; i < oChilds.length; i++){ if(oChilds[i] == ele) return i; } }else { return -1; } } // 根據索引刪除指定的文件對象 var index = getIndex(evt.target.parentNode); ele.removeChild(evt.target.parentNode); if(index < 0){ return; }else { ele.files.splice(index, 1); } console.log('4',ele.files); } } ele.addEventListener('click', removeImg, false); // 上傳圖片 function uploadImg() { console.log(ele.files); var xhr = new XMLHttpRequest(); var formData = new FormData(); for(var i=0, f; f=ele.files[i]; i++){ formData.append('images', f); } console.log('1',ele.files); console.log('2',formData); xhr.onreadystatechange = function (e) { if(xhr.readyState == 4){ if(xhr.status == 200){ options.onSuccess(xhr.responseText); }else { options.onFailure(xhr.responseText); } } } xhr.open('POST', options.path, true); xhr.send(formData); } return uploadImg; }
CSS代碼

#img-container { } #img-container:after { content: '.'; display: block; height: 0; visibility: hidden; overflow: hidden; clear: both; } .img-item { position: relative; float: left; margin-right: 0.1875rem; margin-bottom: 0.1875rem; height: 2.34375rem; width: 2.34375rem; box-sizing: border-box; } .img-thumb { border: 1px solid #000; } .thumb-icon { width: 100%; height: 100%; } .img-up-add { display: table; border: 1px dashed #E0E0E0; } .img-add-icon { display: table-cell; vertical-align: middle; text-align: center; } .img-remove { position: absolute; right: -0.1875rem; top: -0.1875rem; display: block; width: 0.375rem; height: 0.375rem; border-radius: 50%; background: #f7333d; color: #fff; text-align: center; text-decoration: none; font-size: 0.25rem; overflow: hidden; background-clip: padding-box; } #img-file-input { display: none; }
API

/* * @Author: Carl Elias * @Date: 2018-07-11 16:34:38 * @Last Modified by: Carl Elias * @Last Modified time: 2019-03-26 20:04:56 */ var express = require('express'); var fs = require('fs'); var multer = require('multer'); var router = express.Router(); const moment = require('moment'); //設置文件上傳存儲路徑 var uploadDir = `./public/uploads/${moment().format('YYYYMMDD')}`; fs.mkdirSync(uploadDir, { recursive: true }); // recursive 使用遞歸創建目錄,如果父目錄不存在會先創建 const storage = multer.diskStorage({ // 設置上傳中間件 destination: function (req, file, cb) { // 上傳路徑 // console.log(file); cb(null, uploadDir) }, filename: function (req, file, cb) { // 上傳之后的文件名 // console.log(file); cb(null, Date.now() + '-' + file.originalname); } }) //設置multer upload var upload = multer({ storage: storage }).array('images'); //post請求 提交表單 router.post('/file_upload', function (req, res, next) { //多個文件上傳 upload(req, res, function (err) { if (err) { console.error('1.[System] ' + err.message); } else { //循環處理 var imgPath=[]; req.files.forEach(function (i) { //獲取臨時文件的存儲路徑 imgPath.push(i.path); console.log("i.path:",i.path) }); //所有文件上傳成功 //回復信息 var reponse = { message: 'File uploaded successfully', imgPath }; //返回 res.end(JSON.stringify(reponse)); } }); }); module.exports = router
建議參考文檔:
關於本文章頁面效果,暫時仍有一些效果沒有實現,后續將在本人的GitHub中進行持續更新,歡迎大家關注。
最后,再次感謝Express官方,Multer官方,以及前台界面設計者的幫助。