• [Vue]写一个简单的文件上传控件


    这篇将介绍如何写一个简单的基于Vue+Element的文件上传控件。

    控件将具有

    1. 上传队列的列表,显示文件名称,大小等信息,可以显示上传进度实时刷新

    2. 取消上传

     使用Element的uploader控件,上传文件的行为和样式不用自己全部实现,使代码简化。且有足够的扩展性,文件传输请求的代码可以基于axios完全自己重写。我们只用关心核心代码。

    搭建项目框架

    首先建立一个空白的项目,引入Element控件库,具体的操作和使用Element控件库请看官方文档:

    组件 | Element

    后端项目框架的搭建,请阅读:[.Net 6]写一个简单的文件上传控件后端 - 林晓lx - 博客园 (cnblogs.com)

    编写文件上传代码

    编写文件上传的帮助类,新建ajaxRequire.ts并键入以下内容:

    import axios, { CancelTokenSource } from 'axios'
    //发送网络请求
    export const request = async (url: string, methods, data: any, onProgress?: (e)=>void, cancelToken?: CancelTokenSource) => {   
        let token = null
        let timeout = 3000;
        if (cancelToken) {
            token = cancelToken.token
            timeout = 0;
        }
        const service = axios.create()
        const re = await service.request({
            headers: {'Content-Type': 'multipart/form-data'},
            url: url,
            method: methods,
            data: data,
            cancelToken: token,
            timeout: timeout,
            onUploadProgress: function (progressEvent) { //原生获取上传进度的事件
                if (progressEvent.lengthComputable) {
                    if (onProgress) {
                        onProgress(progressEvent);
                    }
                }
            },
        })
        return re as any;
    }
    
    ///获得取消令牌
    export const getCancelToken = () => {
        const source = axios.CancelToken.source();
        return source;
    }

    onUploadProgress回调函数将在数据传输进度变化的时候触发,携带progressEvent 原生获取上传进度事件参数,progressEvent.lengthComputable用于判断是否可以进行进度计算

    axios.CancelToken.source()可以获得一个源,这个源包含一个唯一Id用于标识哪个请求,和一个cancel函数用于取消请求

    编写控件

    在App.vue中添加核心的控件 <el-upload>

    接着添加属性,注意我们将用自己的方法upload替换el-upload中的上传操作,因此设置action="/",

    :http-request="upload",如下:

    <el-upload
          ref="upload"
          :limit="10"
          multiple
          action="/"
          :http-request="upload">
    </el-upload>

    在script中添加上传Dto:一些业务相关的数据在这里定义 比如ownerUserId, fileContainerName等,这些数据可以通过表单与文件数据一并上传

    export class CreateFileDto {
      id: string;
      fileContainerName: string; //文件夹名称
      parentId: string;          //文件的父Id
      ownerUserId: number;        //文件的归属用户Id
      fileName: string;
      mimeType: string;
      fileType: number; //文件类型 0:文件夹,1:普通文件
      file: any;        //文件数据
    }

    method中添加一些帮助类函数:

    methods: {
      successMessage(value = "执行成功") {
          this.$notify({
            title: "成功",
            message: value,
            type: "success",
          });
        },
    
      errorMessage(value = "执行错误") {
          this.$notify.error({
            title: "错误",
            message: value,
          });
        },
    
      FriendlyFileSize(bytes) {
          bytes = parseFloat(bytes);
          if (bytes === 0) return "0B";
          let k = 1024,
            sizes = ["B", "KB", "MB", "GB", "TB"],
            i = Math.floor(Math.log(bytes) / Math.log(k));
          return (bytes / Math.pow(k, i)).toPrecision(3) + sizes[i];
        },
    }

    编写提交前置函数,这里将做验证和生成cancelToken:

    beforeUpload(file) {
          var token = getCancelToken();
          file.cancelToken = token;
          let isLt2M = true;
          if (this.fileSizeLimit < 0) {
            return true;
          }
          isLt2M = file.size / 1024 / 1024 < this.fileSizeLimit;
          if (!isLt2M) {
            this.loading = false;
            this.errorMessage(`"上传文件大小不能超过 ${this.fileSizeLimit}}MB!"`);
          }
          return isLt2M;
    }

     编写upload函数,用于组装请求数据并交给 ajaxRequire 执行上传任务

      async upload(option) {
          this.loaded = true;
          var model = new CreateFileDto();
          var file = option.file;
          model.fileName = file.name;
          model.fileType = 2;
          model.mimeType = file.type;
          model.ownerUserId = 1;
          model.fileContainerName = "Container1";
          model.file = file;
          var fd = new FormData();
    
          Enumerable.from(model).forEach((c) => {
            fd.append(c.key, c.value);
          });
    
          var token = file.cancelToken;
          await request(
            this.uploadUrl,
            "post",
            fd,
            (e) => {
              if (e.total > 0) {
                e.percent = (e.loaded / e.total) * 100;
              }
              option.onProgress(e);
            },
            token
          );
        },

    将token将作为取消传输的入口交给ajaxRequire ,自己也保留这个对象用于发送取消命令,相当于“一式两份”。

    添加el-upload各阶段函数的订阅

    :before-upload="beforeUpload"
    :on-success="handleSuccess"
    :on-remove="handleRemove"
    :on-error="handleError"

        handleSuccess(response, file, fileList) {
          this.successMessage("上传成功");
          this.loading = false;
        },
    
        handleError(e, file, fileList) {
          this.errorMessage(e);
          this.loading = false;
        },
    
        handleRemove(file, fileList) {
          if (file.raw.cancelToken) {
            file.raw.cancelToken.cancel();
          }
        },

    编写上传队列的Html代码:

          <el-button ref="uploadButton">上传</el-button>
          <span slot="file" slot-scope="{ file }">
            <div class="filelist-item">
              <el-row>
                <el-col :span="6" class="file-icon-frame">
                  <i class="el-icon-document file-icon"></i>
                </el-col>
                <el-col :span="18">
                  <el-row>
                    <el-col :span="20">
                      <label class="file-title">
                        {{ file.name }}
                      </label>
                    </el-col>
                    <el-col :span="4" style="text-align: right">
                      <el-button
                        type="danger"
                        icon="el-icon-minus"
                        size="mini"
                        circle
                        @click="handleRemove(file)"
                      ></el-button>
                    </el-col>
                    <el-col :span="24">
                      <label class="file-size">
                        {{ FriendlyFileSize(file.size) }}
                      </label>
                    </el-col>
                    <el-col :span="24">
                      <el-progress
                        :text-inside="true"
                        :stroke-width="26"
                        :percentage="parseInt(file.percentage, 10)"
                        :status="
                          parseInt(file.percentage, 10) == 100 ? 'success' : ''
                        "
                      >
                      </el-progress
                    ></el-col>
                  </el-row>
                </el-col>
              </el-row>
            </div>
          </span>

    运行

    进入后端项目的目录(api),运行:

    dotnet run

    前端项目目录(web),运行

    yarn serve

    运行效果:

     

     

     

     

    完整代码:

    file-uploader-sample/web at master · jevonsflash/file-uploader-sample (github.com)

    项目地址:

    jevonsflash/file-uploader-sample (github.com)

  • 相关阅读:
    【Vue2.0源码学习】生命周期篇-挂载阶段(mount)
    Newman+Jenkins实现接口自动化测试
    进制转换(牛客)(Java语言)
    c++ 中struct和class
    网课查题公众号搭建【详细教程】
    PIL如何批量给图片添加文字水印?
    恭喜磊哥喜提n+1
    c4d渲染动画可以暂停吗?c4d云渲染动画怎么暂停
    记一次有意思的 SQL 实现 → 分组后取每组的第一条记录
    Java基础37 方法重写与覆盖
  • 原文地址:https://www.cnblogs.com/jevonsflash/p/16169944.html