最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • 【文件上传那些事儿】- 01 简单的拖拽上传和进度条

    正文概述 掘金(初心Yearth)   2021-01-15   783

    说在前面的

    在日常的开发工作中,文件上传是一个不可避免的需求,通常我们会使用诸如 elementantd 之类组件库自带的上传组件来实现功能。但若止步于此的话,一旦场景开始变得复杂起来,我们很容易就丧失了进一步解决问题的能力。

    而本系列文章的主旨就帮助我们理解从最基础的文件上传开始,到拖拽上传,添加进度条,大文件上传以及断点续传等等背后的原理,以及如何实现。

    相信读完本系列文章之后,你会对文件上传有更加深刻的认识和理解,那么废话不多说,我们直接开始吧。

    环境搭建

    前端:vue3

    这里没什么需要特别注意的,直接使用 vue-cli 创建项目即可,选项如下:

    ▶ vue create file-upload
    
    Vue CLI v4.5.10
    ? Please pick a preset: Manually select features
    ? Check the features needed for your project: (Press <space> to select, <a> to toggle all, <i> to inve
    rt selection)
    ❯◉ Choose Vue version
     ◉ Babel
     ◯ TypeScript
     ◯ Progressive Web App (PWA) Support
     ◯ Router
     ◯ Vuex
     ◉ CSS Pre-processors
     ◉ Linter / Formatter
     ◯ Unit Testing
     ◯ E2E Testing
    

    依赖如下:

    【文件上传那些事儿】- 01 简单的拖拽上传和进度条

    后端:Koa

    这里除了 Koa 之外,还需要:

    • koa-router:设置路由
    • koa-body:解析 formdata,需要设置 multipart: true
    • koa-static:设置静态目录
    • fs-extra:操作文件(用原生的 fs 也行)

    依赖如下:

    【文件上传那些事儿】- 01 简单的拖拽上传和进度条

    V1.0:基础文件上传

    components 目录下创建 Upload.vue 组件,然后我们就可以开始实现文件上传了。

    首先实现 html 结构,这里需要一个 file 类型的 input,并且要监听它的 change 事件,这样才能获取到文件数据:

    <template>
      <div class="upload">
        <input type="file" name="file" @change="handleFileChange" />
      </div>
    </template>
    
    <script>
    export default {
      name: "Upload",
      setup() {
        const handleFileChange = e => {
          console.log("[+] data", e.target.files);
        };
        return {
          handleFileChange
        };
      }
    };
    </script>
    

    效果如下:

    【文件上传那些事儿】- 01 简单的拖拽上传和进度条

    可以看到,e.target.files 是一个 FileList,这里我们只做基础的单文件上传,所以取其首位即可:

    const [file] = e.target.files;
    

    在选择文件之后,要上传到后端,这里就需要用到网络请求,我们可以使用 axios 来完成这个功能,需要注意的是,如果遇到跨域问题,可以在 vue.config.js 中配置代理:

    devServer: {
      proxy: {
        [process.env.VUE_APP_BASE_API]: {
          target: `${process.env.VUE_APP_SERVER_URL}/api/v1`,
          changeOrigin: true,
          pathRewrite: {
            ["^" + process.env.VUE_APP_BASE_API]: ""
          }
        }
      }
    }
    

    接下来可以做一个简单的测试:

    const res = await axios.get("/dev-api/test");
    console.log("[+] res", res);
    

    结果如下:

    【文件上传那些事儿】- 01 简单的拖拽上传和进度条

    说明网络请求已经没有问题了,那么接下来就可以将文件数据装进 formData 中,发送给后端:

    <template>
      <div class="upload">
        <input type="file" name="file" @change="handleFileChange" />
        <button @click="hanleFileUpload">upload</button>
      </div>
    </template>
    
    <script>
    import axios from "axios";
    import { ref } from "vue";
    export default {
      name: "Upload",
      setup() {
        const file = ref(null);
    
        const handleFileChange = e => {
          const [_file] = e.target.files;
          file.value = _file;
        };
    
        const hanleFileUpload = async () => {
          const formData = new FormData();
          formData.append("name", "file");
          formData.append("file", file.value);
          await axios.post("/dev-api/upload", formData);
        };
    
        return {
          handleFileChange,
          hanleFileUpload
        };
      }
    };
    </script>
    

    当然现在后端功能还没实现,但依然可以测试一下从控制台上看看文件是否真的发送出去了:

    【文件上传那些事儿】- 01 简单的拖拽上传和进度条

    可见文件已经发送出去了,那么在后端创建路由接收即可:

    router.post("/api/v1/upload", async ctx => {
      const file = ctx.request.files.file;
      console.log("[+] file: ", file);
    });
    

    结果如下:

    【文件上传那些事儿】- 01 简单的拖拽上传和进度条

    接下来就只需要将文件移动到公共目录即可:

    router.post("/api/v1/upload", async ctx => {
      const file = ctx.request.files.file;
    + const { name: filename, path: cache } = file;
    + const basename = path.basename(file.path);
    + await fse.move(cache, path.resolve(__dirname, "public/uploads/" + filename));
    + ctx.body = { url: `${ctx.origin}/uploads/${basename}` };
    });
    

    最终结果如下:

    【文件上传那些事儿】- 01 简单的拖拽上传和进度条

    【文件上传那些事儿】- 01 简单的拖拽上传和进度条

    V1.1:拖拽上传

    在基础的铺垫完成之后,我们可以为这个上传新增一些体验向的功能,比如拖拽上传。

    首先,可以给文件上传增加一个边框:

    <template>
      <div class="upload">
        <div class="drag-wrap">
          <input type="file" name="file" @change="handleFileChange" />
        </div>
        <button @click="hanleFileUpload">upload</button>
      </div>
    </template>
    
    <style lang="scss" scoped>
    .drag-wrap {
      display: flex;
      justify-content: center;
      align-items: center;
      height: 100px;
      border: 2px dashed #eee;
    }
    </style>
    

    效果如下:

    【文件上传那些事儿】- 01 简单的拖拽上传和进度条

    接下来,可以简单分析一下:

    • 当文件拖进区域内的时候,给出提示
    • 当文件离开区域的时候,恢复状态
    • 当文件在区域内松开的时候,触发存储文件

    那么这里需要监听三个事件:

    const useDrage = elRef => {
    	const el = elRef.value;
      el.addEventListener("dragover", e => {
        console.log("dragover");
        e.preventDefault();
      });
      el.addEventListener("dragleave", e => {
        console.log("dragleave");
        e.preventDefault();
      });
      el.addEventListener("drop", e => {
        console.log("drop");
        e.preventDefault();
      });
    };
    

    el 则由 ref 指定即可:

    <template>
      <div class="upload">
        <div class="drag-wrap" ref="dragEle">
          <input type="file" name="file" @change="handleFileChange" />
        </div>
      </div>
    </template>
    
    <script>
    import { ref, onMounted } from "vue";
    
    export default {
      name: "Upload",
      setup() {
        const fileData = ref(null);
        const dragEle = ref();
    
        onMounted(() => {
          useDrage(dragEle.value);
        });
    
        return {
          dragEle
        };
      }
    };
    </script>
    

    接下来,只需要做三件事即可:

    • dragover:改变边框颜色
    • dragleave:恢复边框颜色
    • drop:存储文件,并且恢复边框颜色
    el.addEventListener("dragover", e => {
      el.style.borderColor = "red";
      e.preventDefault();
    });
    el.addEventListener("dragleave", e => {
      el.style.borderColor = "#eee";
      e.preventDefault();
    });
    el.addEventListener("drop", e => {
      const [file] = e.dataTransfer.files;
      fileData.value = file;
      el.style.borderColor = "#eee";
      e.preventDefault();
    });
    

    结果如下:

    【文件上传那些事儿】- 01 简单的拖拽上传和进度条

    V1.2:上传进度条

    关于上传进度条,这里我们选择借助 elementprogressaxiosonUploadProgress 来实现,当然,由于只使用了 progresselement 可以按需引入:

    <template>
      <div class="upload">
        <el-progress :percentage="uploadProgress"></el-progress>
      </div>
    </template>
    
    <script>
    import axios from "axios";
    import { ref, onMounted } from "vue";
    
    export default {
      name: "Upload",
      setup() {
        const uploadProgress = ref(0);
    
        const hanleFileUpload = async () => {
          await axios.post("/dev-api/upload", formData, {
            onUploadProgress: progress => {
              const { loaded, total } = progress;
              uploadProgress.value = Number(((loaded / total) * 100).toFixed(2));
            }
          });
        };
    
        return {
          uploadProgress
        };
      }
    };
    </script>
    

    结果如下:

    【文件上传那些事儿】- 01 简单的拖拽上传和进度条

    结束语

    今天的讲解就到这里,下一篇我们将实现二进制层面对于文件类型的校验。


    起源地下载网 » 【文件上传那些事儿】- 01 简单的拖拽上传和进度条

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元