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 🙏

© 2026 – Pkg Stats / Ryan Hefner

@aim-packages/downloader

v0.1.0

Published

基于 node-downloader-helper 的下载器,支持断点续传、自动重试、代理等特性

Downloads

9

Readme

@aim-packages/downloader

由于在 electron 中没有找到好的下载库,以前使用过的 electron-dl 存在一些问题,代理设置也不方便,现在基于 node-downloader-helper 的增强型 Node.js 文件下载库。

✨ 额外功能

本库在 node-downloader-helper 基础上提供了两个额外功能:

  1. 临时文件机制 - 下载过程中使用 .download 后缀的临时文件,下载完成并校验通过后才重命名为最终文件
  2. SHA256 文件校验 - 下载完成后自动验证文件 SHA256 哈希值,确保文件完整性

📦 安装

pnpm add @aim-packages/downloader

🚀 快速开始

import { createDownloader } from '@aim-packages/downloader';

const downloader = createDownloader();

// 基本下载
const filePath = await downloader.download(
  'https://example.com/file.zip',
  '/path/to/destination/file.zip'
);

// 带 SHA256 校验的下载
await downloader.download(
  'https://example.com/file.zip',
  '/path/to/destination/file.zip',
  {
    sha256: 'expected-sha256-hash-value'
  }
);

💡 使用示例

使用 AbortSignal 取消下载

import { createDownloader, DownloadCancelledError } from '@aim-packages/downloader';

const downloader = createDownloader();
const abortController = new AbortController();

// 5 秒后取消下载
setTimeout(() => {
  abortController.abort();
}, 5000);

try {
  await downloader.download(
    'https://example.com/file.zip',
    '/path/to/destination/file.zip',
    {
      signal: abortController.signal
    }
  );
} catch (error) {
  if (error instanceof DownloadCancelledError) {
    console.log('下载已取消');
  }
}

使用代理下载

import { createDownloader } from '@aim-packages/downloader';
import { HttpsProxyAgent } from 'https-proxy-agent';

const downloader = createDownloader();
const proxyAgent = new HttpsProxyAgent('http://proxy.example.com:8080');

await downloader.download(
  'https://example.com/file.zip',
  '/path/to/destination/file.zip',
  {
    proxyAgent
  }
);

🔧 技术细节

临时文件机制

  • 下载过程中使用 .download 后缀的临时文件(例如:file.zip.download
  • 下载完成后进行 SHA256 校验(如果提供)
  • 校验通过后才重命名为最终文件(去掉 .download 后缀)
  • 校验失败自动删除文件并抛出 HashMismatchError

SHA256 校验

  • 如果提供了 sha256 选项,下载完成后会自动计算文件的 SHA256 哈希值
  • 如果哈希值不匹配,会抛出 HashMismatchError 并删除文件
  • 如果哈希值匹配,文件会重命名为最终文件名

📋 错误类型

HashMismatchError

Hash 校验失败错误。

class HashMismatchError extends Error {
  constructor(
    message?: string,
    public readonly expected?: string,
    public readonly actual?: string
  );
}

DownloadCancelledError

下载取消错误。当通过 AbortSignal 取消下载时抛出。

class DownloadCancelledError extends Error {
  constructor(message?: string);
}