express,koa2等node處理前端上傳圖片並儲存到檔案中

sponing發表於2017-12-18

在實際的專案中,圖片的處理往往是最麻煩的,無論是前後臺,我自己也試手了一兩個圖片上傳的小專案,把步驟寫下來,以後自己忘記可以返回來看一下,同時希望能夠幫到小夥伴們...

前端網頁

框架: vue + iview等元件來實現圖片的file功能 + koa2

運用iview的Upload元件

Upload 上傳

upload元件方法方式就不過多一一贅述了,在這裡提幾點關鍵點

<Upload
:on-format-error="handleFormatError"
    :before-upload="handleBeforeUpload"
    :data='uploadData' // 傳參
    multiple
    action="//localhost:6001/blog/admin/edit/image" // http請求路徑
></Upload>

data() {
    return {
        uploadData: {
        	url: '', // 圖片二進位制的data的url
        	articleId: '' // 這個詳情的ID 後臺與他綁在一起
        }
    }
}
// 利用瀏覽器的FileReader的特性壓縮並上傳圖片
handleBeforeUpload() {
    let reader = new FileReader();
    reader.readAsDataURL(file);
    reader.onloadend = (e)=> {
        file.url = reader.result;
        this.uploadData.articleId = this.$route.query.id;
        this.uploadData.url = file.url;
    }
}
複製程式碼

前端總結: 運用iview元件來構造file上傳圖片本身不難,官方瞭解api多次嘗試就可以了

koa後臺node框架

node後臺處理圖片,查了網上資料,採用了formidable的包來處理圖片的,express採用formidable的npm包,而koa2採用了koa-formidable的npm包,兩者沒多大區別,koa-formidable只不過是formidable的再度封裝和運載koa2的框架上而已

我們先看瀏覽器檢視http的請求

express,koa2等node處理前端上傳圖片並儲存到檔案中

圖片的http請求為什麼跟別的不一樣,就是不一樣,我們不一樣,哈哈!別鑽牛角,適應就行了

  • 1.引入koa-formidable
  • 2.下載地址,API
  • let form = formidable.parse(request);
  • 圖片重新命名fs,放入public檔案中
  • 圖片路徑放回給前端

步驟並不複雜,只是本人對於node的模組不太熟悉,比如fs path模組,希望以後多多做node小專案,熟悉起來...

程式碼如下:

// app.js
const Router = require('koa-router');
const router = new Router();
const serve = require("koa-static");

const formidable = require('koa-formidable'); // 圖片處理
const fs = require('fs'); // 圖片路徑
const path = require('path'); // 圖片路徑

app.use(serve(__dirname))  // 設定靜態檔案

// 新建檔案,可以去百度fs模組
let mkdirs = (dirname, callback)=> {
    fs.exists(dirname, function(exists) {
        if (exists) {
            callback();
        } else {
            mkdirs(path.dirname(dirname), function() {
                fs.mkdir(dirname, callback);
            });
        }
    });
};

router.post('/upload/image', function (ctx, next) {
    let form = formidable.parse(request);
    function formImage() {
    	return new Promise((resolve, reject) => {
    		form((opt, {fields, files})=> {
        	let url = fields.url;
        	let articleId = fields.articleId;
        	let filename = files.file.name;
        	console.log(files.file.path);
        	let uploadDir = 'public/upload/';
    	        let avatarName = Date.now() + '_' + filename;
    	        mkdirs('public/upload', function() {
                	fs.renameSync(files.file.path, uploadDir + avatarName); //重新命名
                	resolve(config[env].host + '/' + uploadDir + avatarName)
                	// http://localhost:6001/public/upload/1513523744257_WX20171205-150757.png
                })
            })
    	})
    }
    let url = await formImage();
    return {flag: '1',msg:'',data: url} // 路徑返回給前端
});

app
  .use(router.routes())
  .use(router.allowedMethods());
複製程式碼

要點


1.設定靜態檔案 app.use(serve(__dirname))

根據個人喜好設定靜態路徑
預設的設定方法: 
app.use(serve(__dirname) + '/public') 
resolve(config[env].host + '/upload/' + avatarName)

也可以這樣設定,
app.use(serve(__dirname) + '/public/upload') 
resolve(config[env].host + '/' + avatarName)
複製程式碼

2.fs.exists新建檔案,用於存放圖片

新建一個匿名函式來回撥判斷有無該檔案,有則存入,無則新建檔案

let mkdirs = (dirname, callback)=> {
    fs.exists(dirname, function(exists) {
        if (exists) {
            callback();
        } else {
            mkdirs(path.dirname(dirname), function() {
                fs.mkdir(dirname, callback);
            });
        }
    });
};
複製程式碼

3.koa框架存在非同步,所以必須用await來終結非同步操作

至於async/await可以去百度一下

function formImage(){
    return new Promise((resolve)=> {
        // 處理圖片
        resolve(url)
    })
}
let url = await formImage();
複製程式碼

4.formidable處理圖片,獲取圖片

formidable API

let form = formidable.parse(request);
form((opt, obj)=> {
    // 程式碼如上
})
// 因為obj是個物件並有fields, files兩個引數,那我直接這樣處理:
form((opt, {fields, files})=> {
    // 程式碼如上
    // 上面的程式碼最重要的就是下面這一句 
    // fs.renameSync(opt1,opt2) opt1 form處理的圖片路徑而不是fileds.url二進位制的路徑, opt2則是重新命名的圖片名稱
    fs.renameSync(files.file.path, uploadDir + avatarName);
})

做完就可以 ctx.body = {data: url, msg: '', flag: '1'} 返回給前端
複製程式碼

在另個專案中,同樣的程式碼卻報錯了... 我也是一臉懵逼

以上是在mac筆記本操作,沒有發現錯誤,但在window系統下卻發現圖片上傳不成功,一個致命的錯誤

fs.js:439
  return binding.rename(pathModule._makeLong(oldPath),
                 ^
Error: EXDEV, cross-device link not permitted 'C:\Users\CLi\AppData\Local\Temp\df99513a93a1cbfbc26e076f8ae08b92'
    at Object.fs.renameSync (fs.js:439:18)
複製程式碼

查詢了報錯的原因:是跨分割槽重新命名檔案,會有許可權問題

網友解決方法之一:Node.js中所用的fs.renameSync出錯:Error: EXDEV, cross-device link not permitted

用了以上文章的解決方法: 也報錯了 util.pump is not undefined,這下懵逼了
用了node的pipe流方法

let readStream = fs.createReadStream(files.file.path)
let writeStream = fs.createWriteStream(uploadDir + avatarName);
readStream.pipe(writeStream);
複製程式碼

程式碼如下:

exports.editImages = async(ctx, next)=> {
	let form = formidable.parse(ctx.request);
    form.encoding = 'utf-8';
    form.keepExtensions = true; //保留字尾
    mkdirs('public/upload');
    let imgPlay = new Promise((resolve, reject) => {
	form((opt, {fields, files})=> {
	let articleId = fields.articleId;
	let filename = files.file.name;
        let avatarName = Date.now() + '_' + filename;
        let readStream = fs.createReadStream(files.file.path)
		let writeStream = fs.createWriteStream(uploadDir + avatarName);
		readStream.pipe(writeStream);
                // fs.rename(files.file.path, uploadDir + avatarName); //window報錯了重新命名
        	resolve({
        		url: config[env].host + '/' + uploadDir + avatarName
        	})
        	// http://localhost:6001/public/upload/1513523744257_WX20171205-150757.png
    })
    });
    let imageData = await imgPlay;
	ctx.body = {flag: '1' ,msg:'',data: imageData}
}

複製程式碼

完美解決跨平臺的圖書上傳問題

專案中還有不如,如:前端上傳圖片的壓縮問題,涉及多張上傳問題,都沒有實踐,以後繼續更新不足



這是我試水node的小專案的案例,請多指教

專案github地址:github.com/Jaction/blo…

相關文章