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

enlarge-file-upload

v1.0.33

Published

A tool for chunked file upload with concurrency control.

Downloads

85

Readme

File Upload Tool

请优先下载最新版本库 这是一个用于大文件上传的工具包,提供了系列函数用来支持暂停、恢复、上传进度等功能,内置了错误重传策略,支持断点续传、重传、重试等功能(此库上传基于 axios 库,如果项目不支持 axios,勿用...)。 QQ 讨论群:324710217

安装

npm install enlarge-file-upload

参数介绍

/**
 * 本库计算文件hash值,使用的是 crypto-js 依赖包进行计算,版本为 4.0.0 版
 * 链接:https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.0.0/crypto-js.min.js
 */

/**
 * descript 入参详解
 * @param {Number} chunkSize? 文件单个切片大小,默认5MB
 * @param {Number} concurrency? 并发上传数量,默认5
 * @param {Number} maxRetries? 失败重试次数,默认3
 * @param {Number} startOffset? 断点续传偏移量,默认0
 * @param {Array} includeChunks? 需要上传的切片索引,默认上传所有切片
 * 如果startOffset和includeChunks参数同时存在,且startOffset不为0,默认优先使用startOffset参数
 * @param {Boolen} hash? 是否开启hash计算,默认开启,可选参数
 * @param {Boolen} awaitHash? 是否等待hash值,默认等待,可选参数(仅在hash为true时生效)
 * awaitHash参数,如果上传文件较小时可以开启等待;如果文件超大,建议关闭,大文件hash值计算时间较长,避免等待阻塞主线程
 * @param {Function} uploadFunction 上传函数,必传参数
 * @param {Function} onProgress? 上传进度回调函数,可选
 * @param {Function} onSpeed? 上传速度回调函数,可选
 * @param {Function} onSuccess? 上传成功后回调函数,可选
 * @param {Function} beginHash? 开始计算Hash值回调函数,可选(只会在上传前调用一次,且hash.open必须true)
 * @param {Function} endHash? Hash值计算完毕回调函数,可选(只会在上传前调用一次,且hash.open必须true)
 */
// 参数示例
const config = {
  chunkSize: 5 * 1024 * 1024,
  concurrency: 5,
  maxRetries: 3,
  startOffset:0,
  includeChunks,
  hash: false,
  awaitHash: false,
  uploadFunction,
  onProgress,
  onSuccess
  onSpeed,
  beginHash,
  endHash
};

/**
 * descript fileUploadTool方法返回一个对象,包含上传方法、暂停方法、继续方法等
 * upload 上传方法
 * pause 暂停方法
 * resume 继续方法
 * state 状态对象,包含上传进度、hash值、上传速度等(具体请看对应的TS属性State)
 */
// 方法调用
const { upload, pause, resume, state } = fileUploadTool(config);

案例

注意:下面在 vue3 和 React 中所展示的案例,均用的是 TS 语法进行演示,但他同样适用于非 TS 语法项目,你只需自行删除案例中的 TS 类型声明即可

原生 js 中使用示例

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>文件上传</title>
  </head>
  <script src="https://cdn.jsdelivr.net/npm/enlarge-file-upload/dist/upload.min.js"></script>
  <!-- 引入 Axios 库,用于发送 HTTP 请求 -->
  <script src="https://cdn.bootcdn.net/ajax/libs/axios/1.5.0/axios.js"></script>

  <body>
    <input type="file" id="fileInput" />
    <button id="pauseButton">暂停上传</button>
    <button id="resumeButton">继续上传</button>
    <div id="progress">上传进度:0%</div>
    <div id="speed">上传速度:0 MB/s</div>
    <script>
      // 定义上传函数
      async function uploadFunction({ chunk, index, hash, cancelToken }) {
        const formData = new FormData();
        formData.append("chunk", chunk);
        formData.append("hash", hash);
        formData.append("index", index);
        await axios.post("http://localhost:3000/api/users", formData, {
          cancelToken,
        });
      }

      // 使用示例
      const config = {
        chunkSize: 5 * 1024 * 1024, // 5MB
        concurrency: 5,
        maxRetries: 3,
        // startOffset: 6, // 从索引为10的切片位置开始传
        // includeChunks:[1,6], // 只上传索引为1和6的切片,只有startOffset为0或空时才生效
        uploadFunction,
        onProgress: (progress) => {
          document.getElementById(
            "progress"
          ).innerText = `上传进度:${state.progress.toFixed(2)}%`;
        },
        onSuccess: () => {
          console.log("上传完毕");
        },
        onSpeed: (speed) => {
          document.getElementById("speed").innerText = `上传速度:${speed}`;
        },
      };

      const { upload, pause, resume, state } = createUploader(config);
      const fileInput = document.getElementById("fileInput");
      fileInput.addEventListener("change", () => {
        const file = fileInput.files[0];
        upload(file);
      });
      // 暂停上传
      document.getElementById("pauseButton").addEventListener("click", () => {
        pause();
      });
      // 继续上传
      document.getElementById("resumeButton").addEventListener("click", () => {
        resume();
      });
    </script>
  </body>
</html>

Vue 中使用示例

在 vue3 中使用

<template>
  <div>
    <input type="file" @change="handleFileChange" />
    <button @click="handlePause">暂停上传</button>
    <button @click="handleResume">继续上传</button>
    <div>上传进度:{{ progress.toFixed(2) }}%</div>
    <div>上传速度:{{ speed }}</div>
  </div>
</template>

<script lang="ts" setup>
import { ref, computed } from "vue";
import createUploader from "enlarge-file-upload";
import type { Config, UploadOptions } from "enlarge-file-upload"; // TS导入类型
import axios from "axios";

const progress = ref<number>(0);
const speed = ref<string>("0 MB/s");

// 定义上传函数
async function uploadFunction({
  chunk,
  index,
  hash,
  cancelToken,
}: UploadOptions): Promise<void> {
  const formData = new FormData();
  formData.append("chunk", chunk);
  formData.append("hash", hash);
  formData.append("index", index.toString());
  await axios.post("http://localhost:3000/api/users", formData, {
    cancelToken,
  });
}

// 使用 computed 来生成 uploader 配置,类型为 Config
const uploaderConfig = computed<Config>(() => ({
  chunkSize: 0.01 * 1024 * 1024, // 5MB
  concurrency: 5,
  maxRetries: 3,
  uploadFunction,
  onProgress: (progressValue: number) => {
    progress.value = progressValue;
  },
  onSuccess: () => {
    console.log("上传完毕");
  },
  onSpeed: (speedValue: string) => {
    speed.value = speedValue;
  },
}));

const uploader = ref<ReturnType<typeof createUploader> | null>(null);
uploader.value = createUploader(uploaderConfig.value);

const handleFileChange = (event: Event) => {
  const target = event.target as HTMLInputElement;
  const file = target.files?.[0];
  if (file) {
    uploader.value?.upload(file);
  }
};

const handlePause = () => {
  uploader.value?.pause();
};

const handleResume = () => {
  uploader.value?.resume();
};
</script>

<style scoped></style>

在 vue2 中使用

<template>
  <div>
    <input type="file" @change="handleFileChange" />
    <button @click="handlePause">暂停上传</button>
    <button @click="handleResume">继续上传</button>
    <div>上传进度:{{ progress.toFixed(2) }}%</div>
    <div>上传速度:{{ speed }}</div>
  </div>
</template>

<script>
import Vue from "vue";
import createUploader from "enlarge-file-upload";
import axios from "axios";

export default Vue.extend({
  data() {
    return {
      progress: 0, // 进度
      speed: "0 MB/s", // 速度
      uploader: null, // 上传器实例
    };
  },
  methods: {
    // 定义上传函数
    async uploadFunction({ chunk, index, hash, cancelToken }) {
      const formData = new FormData();
      formData.append("chunk", chunk);
      formData.append("hash", hash);
      formData.append("index", index.toString());
      await axios.post("http://localhost:3000/api/users", formData, {
        cancelToken,
      });
    },

    // 文件选择处理
    handleFileChange(event) {
      const file = event.target.files?.[0];
      if (file && this.uploader) {
        this.uploader.upload(file);
      }
    },

    // 暂停上传
    handlePause() {
      if (this.uploader) {
        this.uploader.pause();
      }
    },

    // 继续上传
    handleResume() {
      if (this.uploader) {
        this.uploader.resume();
      }
    },
  },
  created() {
    const uploaderConfig = {
      chunkSize: 5 * 1024 * 1024, // 5MB
      concurrency: 5,
      maxRetries: 3,
      uploadFunction: this.uploadFunction,
      onProgress: (progressValue) => {
        this.progress = progressValue;
      },
      onSuccess: () => {
        console.log("上传完毕");
      },
      onSpeed: (speedValue) => {
        this.speed = speedValue;
      },
    };

    // 创建上传器实例
    this.uploader = createUploader(uploaderConfig);
  },
});
</script>

<style scoped></style>

React 中使用示例

在 react 中,这里的 useMemo 是必须要使用的,避免组件渲染的时候,重新创建 uploader 对象

import React, { useState, useMemo } from "react";
import createUploader from "enlarge-file-upload";
import type { Config, UploadOptions } from "enlarge-file-upload";
import axios from "axios";

const FileUpload = () => {
  const [progress, setProgress] = useState(0);
  const [speed, setSpeed] = useState("0 MB/s");

  // 定义上传函数
  async function uploadFunction({
    chunk,
    index,
    hash,
    cancelToken,
  }: UploadOptions) {
    const formData = new FormData();
    formData.append("chunk", chunk);
    formData.append("hash", hash);
    formData.append("index", index);
    await axios.post("http://localhost:3000/api/users", formData, {
      cancelToken,
    });
  }

  const uploaderConfig: Config = useMemo(
    () => ({
      chunkSize: 5 * 1024 * 1024, // 5MB
      concurrency: 5,
      maxRetries: 3,
      // startOffset: 6, // 从索引为10的切片位置开始传
      // includeChunks:[1,6], // 只上传索引为1和6的切片,只有startOffset为0或空时才生效
      uploadFunction,
      onProgress: (progress) => {
        setProgress(progress);
      },
      onSuccess: () => {
        console.log("上传完毕");
      },
      onSpeed: (speed) => {
        setSpeed(speed);
      },
    }),
    []
  );

  const uploader = useMemo(
    () => createUploader(uploaderConfig),
    [uploaderConfig]
  );

  const handleFileChange = (event) => {
    const file = event.target.files[0];
    uploader?.upload(file);
  };

  const handlePause = () => {
    uploader?.pause();
  };

  const handleResume = () => {
    uploader?.resume();
  };

  return (
    <div>
      <input type="file" onChange={handleFileChange} />
      <button onClick={handlePause}>暂停上传</button>
      <button onClick={handleResume}>继续上传</button>
      <div>上传进度:{progress.toFixed(2)}%</div>
      <div>上传速度:{speed}</div>
    </div>
  );
};

export default FileUpload;

封装为 react Hooks

一个简单的封装,如有特殊需求,可以基于这个 hooks 进行二次修改

import { useState, useMemo, useCallback } from "react";
import createUploader from "enlarge-file-upload";
import type { Config, UploadOptions } from "enlarge-file-upload";
import axios from "axios";

const useFileUploader = () => {
  const [progress, setProgress] = useState(0);
  const [speed, setSpeed] = useState("0 MB/s");

  const uploadFunction = useCallback(
    async ({ chunk, index, hash, cancelToken }: UploadOptions) => {
      const formData = new FormData();
      formData.append("chunk", chunk);
      formData.append("hash", hash);
      formData.append("index", index);
      await axios.post("http://localhost:3000/api/users", formData, {
        cancelToken,
      });
    },
    []
  );

  const uploaderConfig: Config = useMemo(
    () => ({
      chunkSize: 5 * 1024 * 1024, // 5MB
      concurrency: 5,
      maxRetries: 3,
      // startOffset: 6, // 从索引为10的切片位置开始传
      // includeChunks:[1,6], // 只上传索引为1和6的切片,只有startOffset为0或空时才生效
      uploadFunction,
      onProgress: (progress) => {
        setProgress(progress);
      },
      onSuccess: () => {
        console.log("Upload complete");
      },
      onSpeed: (speed) => {
        setSpeed(speed);
      },
    }),
    [uploadFunction]
  );

  const uploader = useMemo(
    () => createUploader(uploaderConfig),
    [uploaderConfig]
  );

  const uploadFile = useCallback(
    (file) => {
      uploader?.upload(file);
    },
    [uploader]
  );

  const pauseUpload = useCallback(() => {
    uploader?.pause();
  }, [uploader]);

  const resumeUpload = useCallback(() => {
    uploader?.resume();
  }, [uploader]);

  return {
    progress,
    speed,
    uploadFile,
    pauseUpload,
    resumeUpload,
  };
};

export default useFileUploader;

使用封装好的 Hooks 示例

import React from "react";
import useFileUploader from "./useFileUploader.tsx";

const FileUpload = () => {
  const { progress, speed, uploadFile, pauseUpload, resumeUpload } =
    useFileUploader();

  const handleFileChange = (event) => {
    const file = event.target.files[0];
    uploadFile(file);
  };

  return (
    <div>
      <input type="file" onChange={handleFileChange} />
      <button onClick={pauseUpload}>Pause Upload</button>
      <button onClick={resumeUpload}>Resume Upload</button>
      <div>Upload Progress: {progress.toFixed(2)}%</div>
      <div>Upload Speed: {speed}</div>
    </div>
  );
};

export default FileUpload;

建议

如果对此工具包有更好的建议或需要支持新的功能,欢迎提 issue 或者加本人的 QQ:1844119859。