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

@hife/uploader

v2.0.1

Published

## Install ```shell yarn add @hife/uploader ```

Downloads

21

Readme

Web 端文件上传

Install

yarn add @hife/uploader

Example

# 1. 首先 clone 本仓库
git clone [email protected]:hifeteam/uploader.git

# 2. 进入项目安装依赖
cd uploader && yarn

# 3. 开启 node 服务,模拟后端接收请求;也可以修改 server/index.js,自行控制上传请求的响应数据
npm run server

# 4. 开启 storybook 开发环境,查看示例
npm start

API

const uploadOptions = {
  // ...
}
const uploader = new Webuploader(uploadOptions)

1. uploadOptions

import { Canceler } from 'axios';

export interface UploadOptions {
  /**
   * input dom
   */
  dom: HTMLInputElement | HTMLInputElement[];
  /**
   * 是否可以选择多个文件
   * 默认值:false
   */
  multiple?: boolean;
  /**
   * 上传文件的地址
   */
  url: string;
  /**
   * 上传分片的地址(若不指定,则使用 url 作为分片上传的地址)
   */
  uploadChunkUrl?: string;
  /**
   * 文件上传方式
   * 默认值:'post'
   */
  method?: MethodType;
  /**
   * 接受的文件类型
   * 默认值:'*/*'
   * 值参照:https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/file#attr-accept
   * 支持只传文件后缀作为接受的类型
   */
  accept?: string;
  /**
   * 文件的最大大小,单位:B
   */
  maxSize?: number;
  /**
   * 文件的最大数量
   */
  maxCount?: number;
  /**
   * 是否开启分片上传
   * 默认值:false
   */
  chunked?: boolean;
  /**
   * 分片大小,单位:MB
   * 默认值:1
   */
  chunkSize?: number;
  /**
   * 开启分片上传的阈值,单位:MB(文件大小小于该值的时候,不进行分片上传;没有提供值时且开启分片上传时,则所有文件都进行分片上传)
   * 默认值:0
   */
  chunkThreshold?: number;
  /**
   * 是否选择文件后就自动开始上传
   * 默认值:true
   */
  autoUpload?: boolean;
  /**
   * 上传并发数
   * 默认值:1
   */
  threads?: number;
  /**
   * 上传文件出错时自动重试的次数
   * 默认值:2
   */
  retryCount?: number;
  /**
   * 上传分片出错时自动重试次数的次数
   * 默认值:2
   */
  chunkRetryCount?: number;
  /**
   * 文件是否需要 md5 序列化
   * 默认值:false
   */
  md5?: boolean;
  /**
   * 分片是否需要 md5 序列化
   * 默认值:false
   */
  chunkMD5?: boolean;
  /**
   * 文件筛选函数,返回值为通过筛选的文件
   */
  filter?: FilterFunction;
  /**
   * 文件排序函数,返回值为排好序的文件
   */
  sort?: FilterFunction;
  /**
   * 校验完文件(maxSize/maxCount)的回调
   */
  onVerified?: OnVerifiedFn;
  /**
   * 有新的文件被加入队列时的回调
   * filesInfo:被加入到队列的文件信息
   */
  onFilesInfoQueued?: (filesInfo: FileInfo[]) => void;
  /**
   * 文件数量改变、文件状态改变时的回调
   * allFileInfo:所有上传的文件信息
   * statusChangedFileInfo:状态发生的文件信息
   */
  onChange?: (
    allFileInfo: FileInfo[],
    statusChangedFileInfo?: FileInfo
  ) => void;
  /**
   * 上传前的回调,此回调用来对文件进行 md5 序列化、验证等操作
   * fileInfo:文件信息
   * callback:回调函数。当传入 errorMessage 参数,表示有错误,该文件不进行上传;否则进行上传
   */
  onBefore?: (
    fileInfo: FileInfo,
    callback: (errorMessage?: string) => void
  ) => void;
  /**
   * 开始上传的回调
   * fileInfo:文件信息
   */
  onStart?: (fileInfo: FileInfo) => void;
  /**
   * 文件是否上传成功的验证回调(可使用此回调对服务端返回的数据进行验证,看是否真的上传成功了)
   * fileInfo:文件信息
   * res:服务端响应的数据
   * callback:回调函数。当传入 errorMessage 参数,表示有错误,该文件上传是否;否则文件上传成功
   */
  onSuccessVerify?: (
    fileInfo: FileInfo,
    res: unknown,
    callback: (errorMesssage?: string) => void
  ) => void;
  /**
   * 上传成功的回调
   * fileInfo:文件信息
   * res:服务端响应的数据
   */
  onSuccess?: (fileInfo: FileInfo, res?: unknown) => void;
  /**
   * 上传失败的回调
   * error:错误信息
   * info:上传失败的文件(分片)信息
   */
  onError?: (error: Error, info: Info) => void;
  /**
   * 文件上传失败时重试的回调
   * fileInfo:文件信息
   * res:服务端响应的数据
   */
  onRetry?: (fileInfo: FileInfo, res?: unknown) => void;
  /**
   * 上传后的回调(成功或者失败)
   * fileInfo:上传后的文件信息
   */
  onAfter?: (fileInfo: FileInfo) => void;
  /**
   * 上传分片前的回调,此回调用来验证是否可以进行上传
   * chunkInfo:分片信息
   * callback:回调函数。当传入 errorMessage 参数,表示有错误,该文件不进行上传;否则进行上传
   */
  onChunkBefore?: (
    chunkInfo: ChunkInfo,
    callback: (errorMessage?: string) => void
  ) => void;
  /**
   * 开始上传分片的回调
   * chunkInfo:分片信息
   */
  onChunkStart?: (chunkInfo: ChunkInfo) => void;
  /**
   * 分片是否上传成功的验证回调(可使用此回调对后端返回的数据进行验证,看是否真的上传成功了)
   * chunkInfo:分片信息
   * res:服务端响应的数据
   * callback:回调函数。当传入 errorMessage 参数,表示有错误,该分片上传是否;否则分片上传成功
   */
  onChunkSuccessVerify?: (
    chunkInfo: ChunkInfo,
    res: unknown,
    callback: (errorMesssage?: string) => void
  ) => void;
  /**
   * 上传分片成功的回调
   * chunkInfo:分片信息
   * res:服务端响应的数据
   */
  onChunkSuccess?: (chunkInfo: ChunkInfo, res: unknown) => void;
  /**
   * 上传分片失败的回调
   * error:错误信息
   */
  onChunkError?: (error: Error) => void;
  /**
   * 分片上传失败时重试的回调
   * chunkInfo:分片信息
   * res:服务端响应的数据
   */
  onChunkRetry?: (chunkInfo: ChunkInfo, res?: unknown) => void;
  /**
   * 上传分片后的回调(成功或者失败)
   * chunkInfo:分片信息
   */
  onChunkAfter?: (chunkInfo: ChunkInfo) => void;
  /**
   * 一个文件的所有分片都上传完成的回调
   * uploadedFileInfo:文件信息
   * callback:回调
   *   callbackParams:回调参数
   *     errorMessage:当传入 errorMessage 参数,表示有错误,该文件上传失败;否则表示上传成功
   *     isRetry:是否对该文件进行上传重试。可选,默认值:true
   *     fileInfo:文件信息。必传
   *     res:服务端返回的数据。必传
   */
  onChunkComplete?: (
    uploadedFileInfo: FileInfo,
    callback: (callbackParams: {
      // errorMessage 存在时,说明出错了
      errorMessage?: string;
      isRetry?: booelan;
      fileInfo: FileInfo;
      res: unknown;
    }) => void
  ) => void;
  /**
   * 所有文件上传完成的回调
   * uploadedFiles:已上传成功的文件信息
   */
  onComplete?: (uploadedFiles: Array<FileInfo>) => void;
  /**
   * 上传进度改变的回调
   * totalProgress:总进度
   * filesInfo:所有的文件信息
   */
  onProgress?: (totalProgress: number, filesInfo: FileInfo[]) => void;
  /**
   * 请求适配器
   */
  requestAdapter?: RequestAdapterType;
}

export type MethodType = 'post' | 'get';

// 文件(分片)上传的状态:'等待上传' | '正在上传' | '完成上传' | '出错' | '无效'
export type UploadStatus =
  | 'waiting'
  | 'uploading'
  | 'uploaded'
  | 'error'
  | 'invalid';

export interface FileInfo {
  [key: string]: any;
  id: string; // 文件 id
  type: 'file'; // 类型,'file' 表示属于文件
  file: File; // 文件
  chunks?: Array<ChunkInfo>; // 文件的分片
  index: number; // 索引
  md5?: string; // 文件 md5 值
  retryCount: number; // 剩余重试次数
  progress: number; // 上传进度
  status: UploadStatus; // 上传状态
  cancel?: Canceler; // 取消 ajax 请求的函数
  loaded: number; // 已上传的数据量,单位:B
}

export interface ChunkInfo {
  [key: string]: any;
  id: string; // 分片 id
  type: 'chunk'; // 类型,'chunk' 表示属于分片
  belongFile: FileInfo; // 分片所属文件的信息
  chunk: Blob; // 分片
  md5?: string; // 分片的 md5 值
  index: number; // 索引
  retryCount: number; // 剩余重试次数
  status: UploadStatus; // 上传状态
  cancel?: Canceler; // 取消 ajax 请求的函数
  loaded: number; // 已上传的数据量,单位:B
}

export type Info = FileInfo | ChunkInfo;

export interface FilterFunction {
  (files: File[]): File[];
}

// verifiedParams:校验后的参数。当 verifiedParams 为 undefined 时,表示校验没有问题
export type OnVerifiedFn = (verifiedParams?: {
  // 验证未通过的类型:
  // 'MAX_SIZE' 表示有文件未通过 maxSize 的验证
  // 'MAX_COUNT' 表示有文件未通过 maxCount 的验证
  // 'ACCEPT' 表示有文件未通过 accept 的验证(在 firefox 中 accept 有 bug)
  type: 'MAX_SIZE' | 'MAX_COUNT' | 'ACCEPT';
  files: File[]; // 未通过验证的文件信息
}) => void;

2. uploader 实例方法

/**
 * 添加对 input change 事件的监听(当你想用另外一个 file input 选择文件且不想创建新实例时使用。此时新的 file input 选择的文件也处于原来的文件列表中)
 */
uploader.addInputDOM: (HTMLInputElement | HTMLInputElement[]) => void;

/**
 * 获取文件统计信息
 */
uploader.getStats: () => any;

/**
 * 清除文件统计数据
 */
uploader.clearStats: () => any;

/**
 * 将一个文件(分片)设置为成功上传的状态
 * id:文件(分片)的 id
 */
uploader.toSuccessful: (id: string) => void;

/**
 * 通过 id 移除一个文件。移除成功返回 true,移除失败返回 false
 * id:文件的 id
 */
uploader.remove: (id: string) => boolean;

/**
 * 对一个文件(分片)进行重试上传。返回值为 true 时,表示正在重试;否则表示没有重试次数了,不进行重试
 * id:文件(分片)的 id
 */
uploader.retry: (id: string) => boolean;

/**
 * 移除对 input change 事件的监听
 * 注意:在第二次(包含第二次)之后对同一个 input 实例化时,需要先调用方法
 */
uploader.removeEventListener: () => void;

3. 自定义 Request Config

webuploader 使用的是 axios 请求库进行文件上传。有时我们需要修改请求时的一些 选项 来实现一些功能,比如说:

  • 配置 withCredentialstrue,实现在跨域请求时带上 cookie

此时可以使用下面的方法,对 axiosReqeust 进行配置

import { Webuploader, axiosAdapterFactory } from '@hife/uploader';

Webuploader.configure({
  requestAdapter: axiosAdapterFactory({
    withCredentials: true
  })
});

export default Webuploader;
  • 以上配置了一次之后,在别处引用都生效

⚠️ 注意

如果你的项目中也使用了 axios,可能会存在项目中 axios 的全局配置会影响 axiosAdapter 的配置,导致出现 bug

Todolist

  • [ ] 部署 storybook 到 github page,且能够正常查看例子
  • [ ] 测试覆盖率达到 100%
  • [ ] uploader.start():开始上传
  • [ ] uploader.pause():暂停上传

License

MIT