在Web開發中,文件上傳是一個很是常見、很是重要的功能。本文將介紹如何用Node處理上傳的文件。html
因爲如今先後端分離很流行,那麼本文也直接採用先後端分離
的作法。前端界面以下:前端
用戶從瀏覽器中選擇文件,點擊上傳,將發起http請求到服務器,服務器將接受到的文件存儲在服務器硬盤中。node
ajax請求庫採用axios,爲了簡化說明,前端限制上傳的文件類型只能爲圖片,且一次只能上傳一張,有興趣的朋友能夠自行補充,代碼以下:ios
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <script src="https://unpkg.com/axios/dist/axios.min.js"></script> </head> <body> <input type="file" name="file" accept="image/*" onchange="changeImg(event)"/> <button onclick="submit()">上傳</button> <script> let file = '' let fileName = '' function submit() { let data = new FormData() data.append('imgName', fileName) data.append('img', file) axios({ method: 'post', timeout: 2000, url: 'http://localhost:3000/postApi', data: data }) .then(response => { console.log(response.data) }) .catch(error => { console.log(error) }) } function changeImg(e) { file = e.target.files.item(0) // 若是不選擇圖片 if (file === null) { return } fileName = file.name } </script> </body> </html>
這是本文要介紹的重點,爲了用高效流暢的方式來解析文件上傳請求,咱們先引入formidable
庫:git
npm install formidable --save
formidable的流式解析器
讓它成爲了處理文件上傳的絕佳選擇,也就是說它能隨着數據塊的上傳接收它們,解析它們,並吐出特定的部分,相信熟悉流的朋友會很好理解。這種方式不只快,還不會由於須要大量緩衝而致使內存膨脹,即使像視頻這種大型文件,也不會把進程壓垮。
首先,咱們在根目錄下建立myImage文件,用於存放上傳的圖片(注意:若是沒有建立,會致使上傳報錯),接着,咱們建立一個IncomingForm實例form,而且設置存放路徑爲myImage文件夾。代碼以下:github
var http = require('http') var formidable = require('formidable') var server = http.createServer(function(req, res){ // 1 設置cors跨域 res.setHeader('Access-Control-Allow-Origin', '*') res.setHeader('Access-Control-Allow-Headers', 'Content-Type') res.setHeader('Content-Type', 'application/json') // 2 switch (req.method) { case 'OPTIONS': res.statusCode = 200 res.end() break case 'POST': upload(req, res) break } }) function upload(req, res) { // 1 判斷 if (!isFormData(req)) { res.statusCode = 400 res.end('錯誤的請求, 請用multipart/form-data格式') return } // 2 處理 var form = new formidable.IncomingForm() form.uploadDir = './myImage' form.keepExtensions = true form.on('field', (field, value) => { console.log(field) console.log(value) }) form.on('end', () => { res.end('上傳完成!') }) form.parse(req) } function isFormData(req) { let type = req.headers['content-type'] || '' return type.includes('multipart/form-data') } server.listen(3000) console.log('port is on 3000.')
用node app
開啓http服務器後,在前端頁面中上傳一張kitty.jpg
,咱們看到控制檯打印出了前端上傳的imgName屬性:kitty.jpgweb
而且,myImage文件夾目錄下多了一張圖片:ajax
打開一看,正是從前端上傳的那張kitty.jpgchrome
咱們發現,這個默認的文件名稱並非咱們想要的,咱們想改爲以當前時間戳命名的文件,添加的功能代碼以下:npm
var fs = require('fs') form.on('file', (name, file) => { // 重命名文件 let types = file.name.split('.') let suffix = types[types.length - 1] fs.renameSync(file.path, './myImage/' + new Date().getTime() + '.' + suffix) })
再次上傳,發現如今存的照片名稱已經變成咱們想要的格式了。
Formidable的progress事件能給出收到的字節數,以及指望收到的字節數。咱們能夠藉助這個作出一個進度條。
咱們爲上面的程序添加下面的代碼,每次有progress事件激發,就會計算百分比並用console.log()輸出:
form.on('progress', (bytesReceived, bytesExpected) => { var percent = Math.floor(bytesReceived / bytesExpected * 100) console.log(percent) })
再次上傳一張圖片,如今控制檯已經會打印出進度顯示了:
固然,通常狀況下,咱們是要把這個進度傳回到用戶的瀏覽器中去,這對於任何想要上傳大型文件的程序來講是個很棒的特性,而且這是個很適合用Node完成的任務。好比說用WebSocket
協議,或者像Socket.IO這樣的實時模塊,關於Node中使用websocket,後面我會單獨出一篇文章來介紹。
任什麼時候候都不要忘了對程序添加錯誤處理,若是你的程序在重要的時候崩掉了,可能輕則被老闆打屁股,重則拉出去祭天。想象一下,若是用戶上傳的圖片很大,而且用戶的網絡還很慢,那麼上傳的時間會超出前端代碼中設置的請求超時時間2s,服務器就會崩掉,不信?咱們來試一下。
首先,我選擇了一張很大的圖片,5M,而且用chrome瀏覽器將瀏覽器網絡環境設置爲slow 3g,設置方法以下:
f12打開開發者工具,在more tools--network conditions
點擊上傳,咱們看見服務端控制檯的信息以下,服務器崩掉了:
因此,最後咱們加上了錯誤處理,代碼以下:
// 加上錯誤處理,防止用戶網絡慢,或者取消上傳,致使服務器崩掉 form.on('error', err => { console.log(err) res.statusCode = 500 res.end('服務器內部錯誤!') })
如今,相信你已經學會了如何用Node處理文件上傳了,結合前面的那篇用Node提供靜態文件服務的文章,你是否是可以本身摸索着去嘗試作一些有趣的事情了呢?