npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

mpa-uploader

v1.0.0

Published

## 简介

Downloads

1

Readme

MPA-UPLOAD-COMPONENTS 文件上传组件

简介

该组件负责将文件上传到云空间。整个上传文件过程,由以下 6 个阶段组成:

  1. 添加文件
  2. 计算文件的 Hash 值
  3. 初始化文件上传
  4. 文件预分块
  5. 上传文件
  6. 合并文件

名词解释:

  1. tid,即 Task ID, 用于标识任务的唯一 ID
  2. fid,即 FileId,用于标识文件的唯一 ID

上传组件提供了以下 API:

  // 添加一个上传文件任务
  addFiles(items, pid, behavior) {  }
  // 添加一个上传文件夹任务
  addFolder(item, pid, behavior) {  }
  // 移除任务
  remove(tid) {  }
  // 恢复任务
  resume(tid) {  }
  // 暂停任务
  async pause(tid) { }
  // 销毁当前组件
  destroy() { }

同时,组件在工作过程,会触发一些事件,调用方可以根据自己的需求订阅

  // 新的任务,当调用 addFiles/addFolder 方法时触发
  fileUploder.on("new-tasks", (tasks) => {});
  
  // 移除任务,当调用 remove 方法时触发
  fileUploder.on("remove-task", (tid) => { });

  // 任务的状态发生变更,status 取值:ERROR: -1, WAITING: 0, UPLOADING: 1, AUSE: 2, COMPLETE: 3,
  fileUploder.on("task-status-change", (tid, status) => { });

  // 上传文件的状态发生变更 status 取值:ERROR: -1, WAITING: 0, UPLOADING: 1, AUSE: 2, COMPLETE: 3,
  fileUploder.on("file-status-change", (tid, fid, status) => { });

  // 上传速度,其中 e :{tid:'任务ID', rate:'上传速度(单位bytes)', commit:'当前已上传多少(单位bytes)'}, 
  fileUploder.on("upload-speed", (e) => {});

  // 上传进度,其中 e :{tid:'任务ID', uploaded:'已上传字节数', total:'任务总大小'}
  fileUploder.on("upload-progress", (e) => { });

  // 开始计算文件MD5
  fileUploder.on("begin-hash", (tid, fid) => { });

  // 计算文件MD5结束
  fileUploder.on("end-hash", (tid, fid) => { });

  // 计算文件MD5进度:e = { tid: '', fid: '', fileName: '文件名', percent: 进度值[0-100] }
  fileUploder.on("hash-progress", (e) => { });
  
  // 准备初始化文件
  fileUploder.on("begin-prepare", (tid, fid) => { })

  // 初始化文件结束
  fileUploder.on("end-prepare", (tid, fid) => { });

  // 初始化文件结果 file = {metadatas: '文件元数据', md5: '文件hash值', uploadId: '上传ID', blockInfo: '分块信息'}
  fileUploder.on("prepared", (file) => { });
  
  // 准备提交分块信息
  fileUploder.on("begin-commit-blocks", (tid, fid) => {});

  // 提交分块信息结束
  fileUploder.on("end-commit-blocks", (tid, fid) => {});
  
  // 准备上传文件
  fileUploder.on("begin-upload-file", (tid, fid) => {})

  // 上传文件结束
  fileUploder.on("end-upload-file", (tid, fid) => {});

  // 开始合并文件
  fileUploder.on("begin-merge-file", (tid, fid) => {});
  
  // 合并文件结束
  fileUploder.on("end-merge-file", (tid, fid) => {});

  // 上传被取消,一般调用 pause 方法时,会触发该事件
  fileUploder.on("abort", (tid) => {});

  // 上传完成
  fileUploder.on("complete", (task) => {}); 

  // 上传出错 e = {tid, fid, code, message} 
  // code 取值:
  // OK: 0, 
  // USER_ABORT: 1, 
  // CALC_MD5_FAILED: 2, 
  // PREPARE_UPLOAD_FAILED: 3,
  // COMMIT_BLOCKS_FAILED: 4, 
  // UPLOAD_FILE_FAILED: 5, 
  // MERGE_FILE_FAILED: 6,  
  // UNKNOWN: 7,
  fileUploder.on("error", (e) => {  });

初始化组件

使用组件之前,需要现对其实例化操作,实例化需要一些必须的参数,需要提供。

import FileUploader from "./FileUploader";
// 参数
const options = {
  domain: "http://xxxx.com", // 接口域名
  debug: true, // 是否调试模式, 将打印大量信息
  headers?: {
    deviceType: "1",
    spaceId: "0583d",
    userId: "",
    token: "",
    deviceId: "",
  }, // 请求头信息,如果存在 cookies,则可以不用传递
};
// 实例化
const fileUploder = new FileUploader(options);

添加任务

你可以上传文件或者文件夹,添加完成后,系统将通过事件的方式,告知添加的结果,主要包含 2 个 API:

  1. addFiles(items, pid, behavior): 添加文件
  2. addFolder(item, pid, behavior): 添加文件夹

示例

<template>
  <input type="file" @change="handleFileUpload" multiple />
  <input type="file" @change="handleFolderUpload" webkitdirectory />
</template>
    const behavior = "RENAME" // 文件重命名行为
    const pid = "1111" // 要上传文件的上级文件夹 ID

    const fileUploder = new FileUploader(options);

    // 上传文件
    handleFileUpload(event) {
      const target = event.target;
      const files = Array.from(target.files); // 文件列表
      const ok = fileUploader.addFiles(files, pid, behavior);
      if (!ok) return;// 文件添加失败
    }

     // 上传文件夹
    handleFolderUpload(event) {
      const target = event.target;
      const files = Array.from(target.files);
      const ok = fileUploader.addFolder(files, pid, behavior);
      if (!ok) return; // 文件夹添加失败
    }

    // 添加文件后,创建了一个新任务的回调
    fileUploder.on("new-tasks", tasks => {
        // 任务列表
        // 任务对象属性组成
        // {
        //     tid: '', // 任务ID
        //     pid: '', // 上传文件的上级文件夹 ID
        //     isdir: false, // 是否是文件夹
        //     name: 'file.name', // 任务名称
        //     size: 1000, // 要上传文件(夹)的总大小 单位 byte
        //     behavior: 'RENAME', // 文件重名行为
        //     status: 0, // FileStatus 任务状态,见下列表
        // }

        // const FileStatus = {
        //     ERROR: -1, // 错误
        //     WAITING: 0, // 等待上传
        //     UPLOADING: 1, // 上传中
        //     PAUSE: 2, // 暂停中
        //     COMPLETE: 3, // 上传完成
        // };
    })

暂停任务

通过调用 pause,可以取消任务,一旦被取消,将触发 abort 和 task-status-change 事件,调用方可以根据自身逻辑处理渲染层。

pause(tid): 暂停任务

const fileUploder = new FileUploader(options);
fileUploder.on("abort", (tid) => {
  // 任务已暂停
});
fileUploder.on("task-status-change", (tid, status) => {
 // 更新页面上的任务状态
});
const tid = "929w222";
fileUploder.pause(tid);

恢复暂停或者出错的任务

通过调用 resume() 可以继续上传被暂停或者出错的任务,一旦任务开始,将触发 task-status-change 事件

const fileUploder = new FileUploader(options); 
fileUploder.on("task-status-change", (tid, status) => {
 // 更新页面上的任务状态
});
const tid = "929w222";
fileUploder.resume(tid);

删除任务

你可以通过 tid ,即 TaskId 删除任务,删除完成后,将通过事件的方式,通知你已删除完成。

remove(tid) : 删除任务

const fileUploder = new FileUploader(options);
fileUploder.on("remove-task", (tid) => {
  // 任务已删除
});
const tid = "929w222";
fileUploder.remove(tid);

上传文件

上传文件分 5 个步骤,将依次触发下面的事件(注意,不是每个事件一定都会触发)

  1. 计算文件的 Hash 值,触发事件:begin-hash、hash-progress、end-hash
  2. 文件初始化,触发事件:begin-prepare、prepared、begin-prepare
  3. 文件预分块,触发事件:begin-commit-blocks、end-commit-blocks
  4. 文件上传,触发事件:begin-upload-file、upload-speed、upload-progress、end-upload-file
  5. 合并文件,触发事件:begin-merge-file、complete、end-merge-file

示例

 const fileUploader = new FileUploader(options);
 fileUploader.on("begin-hash" (tid, fid) => {
    // 开始计算文件MD5
    // tid, TaskId, 即任务 ID,随机生成的标识
    // fid, FileId, 文件ID,随机生成的标识
 })
  fileUploader.on("end-hash" (tid, fid) => {
    // 计算文件MD5完成
 })
  fileUploader.on("hash-progress" ({ tid, fid, fileName, percent }) => {
    // 正在计算文件MD5
    // fileName 正在计算的文件名
    // percent 当前进度 1-100
 })
 fileUploader.on("complete", (task) => {
    // 文件上传完成
 })

VUE 示例

View

  <div class="hello">
    <span>文件夹</span>
    <input type="file" @change="handleFolderUpload" webkitdirectory multiple />
    <br />
    <span>文件</span>
    <input type="file" @change="handleFileUpload" multiple />
    <br />
    <table border="1">
      <tr v-for="task of tasks" :key="task.tid">
        <td>任务ID: {{ task.tid }}</td>
        <td>状态:{{ task.status }}</td>
        <td>速度:{{ speeder(task.tid) }}</td>
        <td>进度:{{ processer(task.tid) }}</td>
        <td>
          <button @click="onStartTask(task.tid)">开始</button>
          <button @click="onCancelTask(task.tid)">暂停</button>
          <button @click="onRemoveTask(task.tid)">删除</button>
        </td>
      </tr>
    </table>
  </div> 

Javascript:

import FileUploader from "mpa-upload-components";
import { bytesToSize } from "mpa-upload-components/src/utils";

const options = {
  debug: true,
  domain: "http://xxx.com",
  headers: {
    deviceType: "",
    spaceId: "",
    userId: "",
    token: "",
    deviceId: "",
  },
};

const pid = "上级目录的ID";
export default {
  name: "HelloWorld",
  props: {
    msg: String,
  },

  data() {
    return {
      fileMd5: null,
      percent: 0,
      tasks: [],
      speeds: [],
      progresses: [],
      fileUploader: null,
    };
  },

  methods: {
    async handleFolderUpload(event) {
      const target = event.target;
      const files = Array.from(target.files);
      const task = this.fileUploader.addFolder(files, pid);
    },

    async handleFileUpload(event) {
      const target = event.target;
      const files = Array.from(target.files);
      const task = this.fileUploader.addFiles(files, pid);
    },

    onStartTask(tid) {
      this.fileUploader.resume(tid);
    },

    onCancelTask(tid) {
      this.fileUploader.pause(tid);
    },

    onRemoveTask(tid) {
      this.fileUploader.remove(tid);
    },
  },

  computed: {
    speeder() {
      return (tid) => {
        const speed = this.speeds.find((t) => t.tid === tid);
        if (!speed) return "";
        return `${speed.rate}kb/s`;
      };
    },

    processer() {
      return (tid) => {
        const progress = this.progresses.find((t) => t.tid === tid);
        if (!progress) return "";
        const value = ~~((progress.uploaded / progress.total) * 100);
        return `${progress.uploaded}/${progress.total} (${value})%`;
      };
    },
  },

  created() {
    this.fileUploader = new FileUploader(options);
    this.fileUploader.on("new-tasks", (tasks) => {
      this.tasks.push(...tasks);
    });

    this.fileUploader.on("remove-task", (tid) => {
      const index = this.tasks.findIndex((task) => task.tid === tid);
      this.tasks.splice(index, 1);
    });

    this.fileUploader.on("task-status-change", (tid, status) => {
      const task = this.tasks.find((task) => task.tid === tid);
      task.status = status;
    });

    this.fileUploader.on("upload-speed", (e) => {
      const speed = this.speeds.find((t) => t.tid === e.tid);
      if (speed) {
        speed.rate = bytesToSize(e.rate);
        speed.commit = e.commit;
      } else {
        this.speeds.push(e);
      }
    });

    this.fileUploader.on("upload-progress", (e) => {
      const progress = this.progresses.find((t) => t.tid === e.tid);
      if (progress) {
        progress.total = e.total;
        progress.uploaded = e.uploaded;
      } else {
        this.progresses.push(e);
      }
    });
  },
};