在實際的專案中,圖片的處理往往是最麻煩的,無論是前後臺,我自己也試手了一兩個圖片上傳的小專案,把步驟寫下來,以後自己忘記可以返回來看一下,同時希望能夠幫到小夥伴們...
前端網頁
框架: vue + iview等元件來實現圖片的file功能 + koa2
運用iview的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的請求
圖片的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處理圖片,獲取圖片
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…