- 客戶端的準(zhǔn)備:采用Vue+iview的Upload組件實(shí)現(xiàn)。
<template>
<div>
<Upload
ref="upload"
:show-upload-list="true"
:default-file-list="defaultList"
:on-success="handleSuccess"
:max-size="2048"
:on-format-error="handleFormatError"
:on-exceeded-size="handleMaxSize"
:before-upload="handleBeforeUpload"
multiple
type="drag"
action="http://jsonplaceholder.typicode.com/posts/">
<div style="padding: 20px 0">
<Icon type="ios-cloud-upload" size="52" style="color: #3399ff"></Icon>
<p>Click or drag files here to upload</p>
</div>
</Upload>
</div>
<script>
export default {
data() {
return {
defaultList:[]
}
},
methods:{
handleBeforeUpload(){
},
handleSuccess(){
},
handleFormatError(){
},
handleMaxSize(){
}
}
}
</script>
</template>
可以發(fā)現(xiàn),上面的代碼我只是從官網(wǎng)上隨便拷貝了一下做了簡(jiǎn)單的修改,但已經(jīng)可以將文件上傳到iview提供的服務(wù)器上,也就是說(shuō)現(xiàn)在只需要修改一下action的值為Koa2提供的API即可,至于一些后續(xù)的擴(kuò)展待API實(shí)現(xiàn)了后再慢慢的講解。
- 服務(wù)器端的處理:采用Koa2實(shí)現(xiàn)
輸入npm init,初始化項(xiàng)目
輸入npm install koa2 --save下載koa2的依賴包
輸入npm install koa-router -save下載koa2路由依賴包
const Koa=require('koa2');
const app=new Koa;
const Router=require('koa-router');
var router=new Router;
const fs=require('fs');
function parsePostData(ctx){
return new Promise((resolve,reject)=>{
try{
let postdata;
ctx.req.on('data',(data)=>{
postdata=data;
})
ctx.req.addListener('end',()=>{
let filename=Date.now().toString()+'.文件的后綴名';
fs.writeFile('./upload/'+filename,postdata,function(err){
if(err){
reject(err);
}
resolve(filename);
});
});
}catch(err){
reject(err);
}
})
}
router.post('/upload',async ctx=>{
var fileName=await parsePostData(ctx);
ctx.body={
code:200,
fileName:fileName
}
});
app.use(router.routes()).use(router.allowedMethods());
app.listen(3000,()=>{
console.log("服務(wù)器已開(kāi)啟!");
});
從代碼中可以看出,上面的代碼遇到了一個(gè)問(wèn)題,就是如何獲取文件的后綴名,另外還有一個(gè)隱藏的問(wèn)題,假設(shè)認(rèn)為后綴名就是.txt,在上傳完文件后,打開(kāi)服務(wù)器上保存的文件,會(huì)發(fā)現(xiàn)存在一些原來(lái)文本內(nèi)容當(dāng)中不存在的內(nèi)容,這是因?yàn)楦鶕?jù)RFC 1867協(xié)議導(dǎo)致的,不過(guò)目前本人無(wú)法解決,所以在這里我就放棄了該思路,如果有了解的小伙伴歡迎私信與我探討。
自己寫(xiě)不出來(lái)但是可以發(fā)現(xiàn)有現(xiàn)成的koa-multer依賴包可以用,故而
輸入npm install koa-multer --save下載koa-multer
輸入npm install koa2-cors --save解決跨域的問(wèn)題
輸入npm install koa-static --save實(shí)現(xiàn)靜態(tài)資源訪問(wèn)
const Koa=require('koa2');
const app=new Koa;
const fs=require('fs');
const path = require('path')
const static = require('koa-static')
const staticPath = './upload'
app.use(static(
path.join( __dirname, staticPath)
))
//解決跨域
const cors=require('koa2-cors');
app.use(cors());
const Router=require('koa-router');
var router=new Router;
//引用multer實(shí)現(xiàn)文件上傳與下載
const multer=require('koa-multer');
//配置單文件上傳的路徑
var storage = multer.diskStorage({
//定義文件保存路徑
destination:function(req,file,cb){
cb(null,'./upload/');//路徑根據(jù)具體而定。如果不存在的話會(huì)自動(dòng)創(chuàng)建一個(gè)路徑
}, //注意這里有個(gè),
//修改文件名
filename:function(req,file,cb){
var fileFormat = (file.originalname).split(".");
cb(null,Date.now() + "." + fileFormat[fileFormat.length - 1]);
}
})
var upload = multer({ storage: storage });
router.post('/upload',upload.single('file'),async (ctx,next)=>{
ctx.body={
filename:ctx.req.file.filename
}
});
app.use(router.routes()).use(router.allowedMethods());
app.listen(3000,()=>{
console.log("服務(wù)器已開(kāi)啟!");
});
利用multer就很快速的實(shí)現(xiàn)了文件的上傳功能,再回過(guò)頭來(lái)完善客戶端的功能。
- 客戶端的完善
將action的值修改為http://localhost:3000/upload,打開(kāi)頁(yè)面,任意上傳幾個(gè)文件,可以發(fā)現(xiàn)一點(diǎn)問(wèn)題都沒(méi)有。
客戶端上傳效果
增加文件上傳個(gè)數(shù)的限制
根據(jù)文檔,可以發(fā)現(xiàn)存在一個(gè)before-upload的鉤子函數(shù),即文件上傳之前的鉤子函數(shù),所以該需求的實(shí)現(xiàn)方法如下。
handleBeforeUpload(){
const check = this.$refs.upload.fileList.length < 2;
if (!check) {
this.$Notice.warning({
title: '最多只能上傳兩個(gè)文件!'
});
}
return check;
},
阻止文件上傳,當(dāng)點(diǎn)擊某一個(gè)按鈕的時(shí)候再批量上傳
阻止文件上傳根據(jù)官方文檔可以知道只需在before-upload鉤子函數(shù)內(nèi)返回false即可,故而利用
this.$refs.upload.upload(file);
是實(shí)現(xiàn)不了上傳的,那么只能自己發(fā)請(qǐng)求去處理了。
<template>
<div>
<Upload
ref="upload"
:show-upload-list="false"
:max-size="2048"
:before-upload="handleBeforeUpload"
multiple
type="drag"
action="http://localhost:3000/upload">
<div style="padding: 20px 0">
<Icon type="ios-cloud-upload" size="52" style="color: #3399ff"></Icon>
<p>Click or drag files here to upload</p>
</div>
</Upload>
<Button type="primary" @click="startPost">開(kāi)始上傳</Button>
<div v-for="(item,index) in uploadList" :key="index">
<Progress :percent="item.percentage"></Progress>
</div>
</div>
</template>
<script>
import axios from 'axios'
export default {
data() {
return {
uploadList:[]
}
},
mounted(){
this.uploadList=this.$refs.upload.fileList;
},
methods:{
startPost(){
this.$refs.upload.fileList.forEach(file=>{
//this.$refs.upload.upload(file); 由于已經(jīng)在handleBeforeUpload方法當(dāng)中阻止了,所以該方法上傳不了
var formFile = new FormData();
let config = {
headers: {
'Content-Type': 'multipart/form-data'
},
onUploadProgress: (progressEvent) => {
// 使用本地 progress 事件
if (progressEvent.lengthComputable) {
let num = Math.round((progressEvent.loaded / progressEvent.total) * 100)
// 使用某種 UI 進(jìn)度條組件會(huì)用到的百分比
file.percentage = num;
this.$nextTick(()=>{
//在修改數(shù)據(jù)之后使用 $nextTick,則可以在回調(diào)中獲取更新后的 DOM
this.uploadList=[...this.$refs.upload.fileList]
});
}
}
}
formFile.append("file", file);
axios.post("http://localhost:3000/upload", formFile, config).then(res=> {
console.log(res,"上傳成功!");
}).catch(function (error) {
console.log(error,"上傳失敗!");
});
});
},
handleBeforeUpload(file){
const check = this.$refs.upload.fileList.length < 2;
if (!check) {
this.$Notice.warning({
title: '最多只能上傳兩個(gè)文件!'
});
return check;
}
file.percentage=0;
this.$refs.upload.fileList.push(file);
return false;
}
}
}
</script>
最后至于如何在樣式上設(shè)計(jì)可以參考官網(wǎng)最后一個(gè)案例。