[Vue]写一个简单的文件上传控件
阅读原文时间:2022年04月20日阅读:1

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

控件将具有

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

2. 取消上传

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

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

组件 | Element

后端项目框架的搭建,请阅读:

编写文件上传的帮助类,新建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中添加核心的控件

接着添加属性,注意我们将用自己的方法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)