美文网首页Vue.js开发技巧前端开发笔记Vue.js
Vue+Koa2实现文件的上传和下载

Vue+Koa2实现文件的上传和下载

作者: lk儒家 | 来源:发表于2018-10-22 13:59 被阅读17次
    • 客户端的准备:采用Vue+iview的Upload组件实现。
    <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="//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>
    

    可以发现,上面的代码我只是从官网上随便拷贝了一下做了简单的修改,但已经可以将文件上传到iview提供的服务器上,也就是说现在只需要修改一下action的值为Koa2提供的API即可,至于一些后续的扩展待API实现了后再慢慢的讲解。

    • 服务器端的处理:采用Koa2实现

    输入npm init,初始化项目
    输入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("服务器已开启!");
    });
    

    从代码中可以看出,上面的代码遇到了一个问题,就是如何获取文件的后缀名,另外还有一个隐藏的问题,假设认为后缀名就是.txt,在上传完文件后,打开服务器上保存的文件,会发现存在一些原来文本内容当中不存在的内容,这是因为根据RFC 1867协议导致的,不过目前本人无法解决,所以在这里我就放弃了该思路,如果有了解的小伙伴欢迎私信与我探讨。
    自己写不出来但是可以发现有现成的koa-multer依赖包可以用,故而

    输入npm install koa-multer --save下载koa-multer
    输入npm install koa2-cors --save解决跨域的问题
    输入npm install koa-static --save实现静态资源访问

    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实现文件上传与下载
    const multer=require('koa-multer');
    
    //配置单文件上传的路径
    var storage = multer.diskStorage({
        //定义文件保存路径
        destination:function(req,file,cb){
            cb(null,'./upload/');//路径根据具体而定。如果不存在的话会自动创建一个路径
        },                       //注意这里有个,
        //修改文件名
        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("服务器已开启!");
    });
    

    利用multer就很快速的实现了文件的上传功能,再回过头来完善客户端的功能。

    • 客户端的完善
      将action的值修改为http://localhost:3000/upload,打开页面,任意上传几个文件,可以发现一点问题都没有。
      客户端上传效果
    增加文件上传个数的限制

    根据文档,可以发现存在一个before-upload的钩子函数,即文件上传之前的钩子函数,所以该需求的实现方法如下。

          handleBeforeUpload(){
            const check = this.$refs.upload.fileList.length < 2;
            if (!check) {
                this.$Notice.warning({
                    title: '最多只能上传两个文件!'
                });
            }
            return check;
          },
    
    阻止文件上传,当点击某一个按钮的时候再批量上传

    阻止文件上传根据官方文档可以知道只需在before-upload钩子函数内返回false即可,故而利用

    this.$refs.upload.upload(file);
    

    是实现不了上传的,那么只能自己发请求去处理了。

    <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">开始上传</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); 由于已经在handleBeforeUpload方法当中阻止了,所以该方法上传不了
              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 进度条组件会用到的百分比
                        file.percentage = num;
                        this.$nextTick(()=>{
                          //在修改数据之后使用 $nextTick,则可以在回调中获取更新后的 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: '最多只能上传两个文件!'
                });
                return check;
            }
            file.percentage=0;
            this.$refs.upload.fileList.push(file);
            return false;
            
          }
        }
      }
    </script>
    

    最后至于如何在样式上设计可以参考官网最后一个案例。

    相关文章

      网友评论

        本文标题:Vue+Koa2实现文件的上传和下载

        本文链接:https://www.haomeiwen.com/subject/ieuwzftx.html