express,koa2等node處理前端上傳圖片並保存到文件中

在實際的項目中,圖片的處理每每是最麻煩的,不管是先後臺,我本身也試手了一兩個圖片上傳的小項目,把步驟寫下來,之後本身忘記能夠返回來看一下,同時但願可以幫到小夥伴們...前端

前端網頁

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

運用iview的Upload組件vue

Upload 上傳node

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

<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屢次嘗試就能夠了github

koa後臺node框架

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

咱們先看瀏覽器查看http的請求

圖片的http請求爲何跟別的不同,就是不同,咱們不同,哈哈!別鑽牛角,適應就好了npm

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

步驟並不複雜,只是本人對於node的模塊不太熟悉,好比fs path模塊,但願之後多多作node小項目,熟悉起來...api

代碼以下:瀏覽器

// 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新建文件,用於存放圖片

新建一個匿名函數來回調判斷有無該文件,有則存入,無則新建文件bash

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…

相關文章
相關標籤/搜索