[Vue]寫一個簡單的文件上傳控制項

這篇將介紹如何寫一個簡單的基於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中添加核心的控制項 <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)