Vue+Koa2實(shí)現(xiàn)文件的上傳和下載

  • 客戶端的準(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è)案例。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,001評(píng)論 6 537
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,786評(píng)論 3 423
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人,你說(shuō)我怎么就攤上這事。” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 176,986評(píng)論 0 381
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我,道長(zhǎng),這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 63,204評(píng)論 1 315
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 71,964評(píng)論 6 410
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 55,354評(píng)論 1 324
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,410評(píng)論 3 444
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 42,554評(píng)論 0 289
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,106評(píng)論 1 335
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 40,918評(píng)論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 43,093評(píng)論 1 371
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,648評(píng)論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,342評(píng)論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 34,755評(píng)論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 36,009評(píng)論 1 289
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 51,839評(píng)論 3 395
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 48,107評(píng)論 2 375

推薦閱讀更多精彩內(nèi)容