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 🙏

© 2025 – Pkg Stats / Ryan Hefner

@sharedkit/uploader

v0.1.6

Published

weapp uploader tools.

Downloads

5

Readme

Uploader

https://github.com/WumaCoder/uploader

特点

  • 自定义传输处理器
  • 事件钩子处理
  • 简单
  • 流的方式调用

安装

yarn add @sharedkit/uploader 
# npm i @sharedkit/uploader

简单使用

import {
  Uploader,
  UploadHook,
  RemoteHook,
  LocalChooseUploadHandler,
  RemoteUploadHandler,
} from "@sharedkit/uploader";
// 或者 `const { Uploader, LocalChooseUploadHandler } = require('@sharedkit/uploader');`

async function runDemo() {
  const option = { // 选项与传入的处理器有关,因为这个直接给处理器传入的
    type: "file", // 传给wx.chooseMessageFile的type选项
    exts: ["xlsx"],// 传给wx.chooseMessageFile的extension选项
    prefix: `/score`, // 生成资源路径的前缀
    count: 1, // 传给wx.chooseMessageFile的count选项
    uploadFileHandler: (files) =>    // 上传处理程序的钩子,files是UploadAliyunFile类型,可以直接用于aliyunOss函数
        new Promise((resolve, reject) => {
          aliyunOss(files, resolve, reject);
     		}),
  };
  const uploader = new Uploader(LocalChooseUploadHandler, option); // 创建上传器,第一个参数是上传处理器的构造函数,option 是穿给他的参数
  const res = await uploader.upload().wait(); // upload是上传方法,wait是等传输完成,最后返回FileMeta[]

  console.log(res); // FileMeta[], 参考 FileMeta 类型
}

runDemo();

在班级小管家里可以使用更好的方式,使用/utils/UploaderFactory.js里的工厂函数,他会自动埋点发日志,自动传入类似于uploadFileHandler这样的方法,用户只需要关注使用即可,具体使用可以看example/下的例子。

API

核心类

Class Uploader

创建上传处理器

export declare class Uploader<O> {
    /**
     * @param  {UploadHandlerConstruction<O>} UploadHandler // 上传处理器
     * @param  {O} option? 传给 UploadHandler 的选项
     */
    constructor(UploadHandler: UploadHandlerConstruction<O>, option?: O);
    /**
     * 同上
     * @param  {UploadHandlerConstruction<O>} UploadHandler
     * @param  {O} option?
     * @returns Uploader
     */
    loadUploadHandler(UploadHandler: UploadHandlerConstruction<O>, option?: O): Uploader<O>;
    /**
     * 开始上传
     * @returns Uploader
     */
    upload(): Uploader<O>;
    /**
     * 监听一个钩子
     * @param  {H} hook 钩子类型
     * @param  {HookCb<H>} cb 回调函数
     * @returns Uploader
     */
    on<H extends UploadHook>(hook: H, cb: HookCb<H>): Uploader<O>;
    /**
     * 移除一个钩子
     * @param  {H} hook
     * @param  {HookCb<H>} cb
     * @returns Uploader
     */
    off<H extends UploadHook>(hook: H, cb: HookCb<H>): Uploader<O>;
    /**
     * 监听一个钩子, 但只监听一次
     * @param  {H} hook 钩子类型
     * @param  {HookCb<H>} cb 回调函数
     * @returns Uploader
     */
    once<H extends UploadHook>(hook: H, cb: HookCb<H>): Uploader<O>;
    /**
     * 等待Uplaoder上传完成
     * */
    wait(): Promise<FileMeta[]>;
    /**
     * 获取或者修改选项
     * @param  {O} option? 传入的选项
     */
    option(option?: O): O;
    /**
     * 销毁
     * @returns void
     */
    destroy(): void;
}

Class abstract UploadHandler

这个类是用于自定义上传处理器的时候使用,如果您不写上传处理器是不需要了解的,如何写上传处理器,请查看LocalChooseUploadHandler源码

export declare abstract class UploadHandler<T, CUH = any, H = CUH | UploadHook> {
    constructor(option: T);
    option(option?: T): T;
    /**
     * 获取事件处理器
     * @returns EventHub
     */
    hook(): EventHub<H>;
    /**
     * 需要重写的方法,如果不重写会报错,核心的上传方法
     * @returns Promise
     */
    upload(): Promise<FileMeta[]>;
    /**
     * 需要重写的方法,如果不重写会报错,销毁方法
     * @returns void
     */
    destroy(): void;
}

class FileMeta

最后返回的文件类型,远程上传只会有url和ext,之后会支持

export interface FileMeta {
  name?: string; // 文件名
  size?: number; // 文件大小
  type?: string; // 文件类型
  ext?: string; // 文件扩展
  url?: string; // 文件在OSS的完整地址
  urlPath?: string; // 文件在OSS的上除去域名的部分
  path?: string; // 文件在本地到位置
  time?: number; // 文件在会话的时间
}

上传处理器

class LocalChooseUploadHandler

本地上传处理器

export class LocalChooseUploadHandlerOption { // 传入的选项
  exts: string[] = []; // 限制文件后缀,最后会传给微信API
  count: number = 1; // 限制文件数量,最后会传给微信API
  type: "all" | "video" | "image" | "file" = "all"; // 类型,给微信API的
  cate?: Cate; // 会传给 aliyunOss 这个方法
  size?: number = 1024 * 1024 * 3; // B, default 3MB 限制大小
  prefix?: string = ""; // 资源路径前缀
  uploadFileHandler: uploadFileHandler; // 上传文件的钩子函数, 在工厂函数里会自动写入
}

export type uploadFileHandler = (files: UploadAliyunFile[]) => Promise<string> | void;
// 上传处理器

type Cate =
  | "record" // 录音
  | "video" // 视频
  | "img" // 图片
  | "answer_img" // 答题图片
  | "file" // 文件,除图片视频录音
  | "album" // 网盘相册
  | "disk"; // 网盘

export class UploadAliyunFile {
  // TODO: 这是是 `utils/uploadoss/uploadAliyun.js` uploadFile 第一个参数的类型
  //       目前的临时解决方案,之后封装了API之后修改
  cate: Cate;
  file: string;
  new_name: string;
  size: number;
  duration?: number;
}

class RemoteUploadHandler

远程上传器

export class RemoteUploadHandlerOption {
  exts: string[] = []; // 限制文件后缀
  count: number = 1; // 限制文件数量
  // type: "all" | "video" | "image" | "file" = "all";
  // cate?: Cate;
  // size?: number = 1024 * 1024 * 3; // B, default 3MB
  // prefix?: string = "";
  maxReadAssetUrlTimes?: number = 1000; // 限制轮询次数
  sleepInterval?: number = 1000; // ms default 1s 轮询间隔
  createCodeHandler: CreateCodeHandler; // 创建传输码的钩子
  removeCodeHandler: RemoveCodeHandler; // 移除传输码的钩子
  readAssetUrlHandler: ReadAssetUrlHandler; // 读取传输码的钩子
}


export type CreateCodeHandler = () => Promise<string>;
export type RemoveCodeHandler = (code) => Promise<void> | void;
export type ReadAssetUrlHandler = (
  string
) => Promise<string[] | undefined | false>;
事件
export enum RemoteHook {
  CREATED_CODE = "createdCode", // 创建完传输码之后
}

事件

export enum UploadHook {
  CREATED = "created", // 创建完成后
  // BEFORE_UPLOAD = 'beforeUpload',
  UPLOADED = "uploaded", //上传完成后
  // ABOUT = "about",
  ERROR = "error", // 报错之后
  // PROCESS = "process",
  DESTROYED = "destroyed", // 销毁之后
  WAIT = "wait", // 上传完成之后,无论成功或失败,与uploaded回调参数有不同
}

工厂

注意:文件位置 /utils/UploaderFactory.js

定义

import {
  Uploader,
  UploadHook,
  RemoteHook,
  LocalChooseUploadHandler,
  RemoteUploadHandler
} from "@sharedkit/uploader";

const aliyunOss = require("./uploadoss/uploadAliyun.js");

let {
  getUniqueTransCode,
  getTransCodeContent,
  delTransCodeContent,
} = require("./request");

export class UploaderFactory {
  /**
   * 创建上传器
   * @param  { UploadHandlerConstruction } UploadHandler 上传处理器,LocalChooseUploadHandler 或者 RemoteUploadHandler
   * @param  { object } opts // 上传选项
   * @param  { object } logData // 上传携带的埋点数据 { cate } cate 从哪个功能模块上传,如 成绩,网盘
   */
  static create(UploadHandler, opts = {}, logData = {}) {
    const option = {
      uploadFileHandler: (files) =>
        new Promise((resolve, reject) => {
          // 上传处理程序的钩子
          aliyunOss(files, resolve, reject);
        }),
      createCodeHandler: () => getUniqueTransCode().then((res) => res.data),
      // 创建传输码的钩子
      removeCodeHandler: (code) =>
        delTransCodeContent({
          // 移除传输码的钩子
          trans_code: code,
        }),
      readAssetUrlHandler: async (code) => {
        // 使用传输码读取资源的钩子
        const res = await getTransCodeContent({
          trans_code: code,
        });

        if (res.code !== 0 || res.data === "1") {
          return;
        }

        return res.data.split(";");
      },
      ...opts,
    };

    const uploader = new Uploader(UploadHandler, option);

    uploader.on(UploadHook.CREATED, (data) => {
      console.log("created hook:", data);
      Log.ok({
        type: "created",
        message: "Uploader创建成功",
        ...logData,
      });
    });

    uploader.on(RemoteHook.CREATED_CODE, (code) => {
      console.log("createdCode hook:", code);
      Log.ok({
        type: "createdCode",
        message: "传输码创建成功",
        code,
        ...logData,
      });
    });

    uploader.on(UploadHook.UPLOADED, (files) => {
      console.log("uploaded hook:", files);
      Log.ok({
        type: "uploaded",
        message: "传输完成",
        files,
        ...logData,
      });
    });

    uploader.on(UploadHook.ERROR, (err) => {
      console.log("error hook:", err, err.file);
      Log.err({
        type: err.type,
        message: err.message,
        files: err.file.length ? err.file : [err.file],
        ...logData,
      });
    });

    return uploader;
  }
}
const app = getApp();

class Log {
  static ok(data) {
    Log.send({
      status: "ok",
      ...data,
    });
  }

  static err(data) {
    Log.send({
      status: "error",
      ...data,
    });
  }

  /**
   * data
   *  num
   *  files:FileMeta[]
   */
  static send(data) {
    const { files = [] } = data;

    const logData = delNullProperty({
      id: "导入上传",
      cate: data.cate, //是哪个模块上传的,成绩还是花名册还是网盘还是哪里
      num: files.length, //传了几个文件
      openid: app.globalData.currentUser.openid,
      name: JSON.stringify(files.map((file) => file.name)), //多个的话,怎么表示?
      url: JSON.stringify(files.map((file) => file.url)), // 文件地址
      op_type: JSON.stringify(files.map((file) => file.ext)), // 上传文件的类型
      file_size: files.reduce((p, n) => p + n.size, 0),
      status: data.status, // status  ok | error
      message: data.message, // 消息
      create_at: Date.now(), // 创建时间
      code: data.code, // 上传Code
      platform: "mini", // 上传的平台, mini 小程序, web网页
      type: data.type, // 阶段, created 实例创建,createCode Code生成,uploaded 上传完成
    });

    console.log(logData);

    app.addEvent(logData);
  }
}

//遍历删除对象中的空值属性
function delNullProperty(obj) {
  for (const key in obj) {
    if (Object.hasOwnProperty.call(obj, key)) {
      const value = obj[key];
      const caseTrue =
        value === undefined ||
        value === null ||
        (typeof value === "number" && value === 0) ||
        (value instanceof Array && value.length === 0) ||
        value === "[]" ||
        value === "{}";

      if (caseTrue) {
        delete obj[key];
      }
    }
  }

  return obj;
}

使用

详细查看 packages/uploader/example

const option = {
  type: "file",
  exts: ["xlsx"],
  prefix: `/score/${cls.unique_code}`,
  count: 1,
};

const uploader = UploaderFactory.create(
  LocalChooseUploadHandler,
  option
);
const files = await uploader.upload().wait();