在线咨询
Skip to content

任务 API ​

任务 API(xunlei.tasks)是微应用的核心能力:创建下载任务、查询进度、访问已下载文件。页面与事件脚本均可调用完整的任务 API,每个方法需要在 manifest 中声明对应的具体权限;事件上下文的任务控制方法另见钩子事件。

任务模型 ​

一个任务(Task)的关键结构:

ts
interface Task {
  type: 'single'; // 任务类型:普通任务为 single,任务组为 group
  id: string; // 任务 ID
  protocol: 'http' | 'bt' | 'ed2k'; // 协议类型
  name: string; // 显示名称
  meta: {
    req: Request; // 下载请求(详情不返回 extra 请求头)
    res: Resource; // 资源信息(文件列表、大小等)
    opts: Options; // 下载选项(详情不返回 extra)
  };
  status: TaskStatus; // 任务状态
  progress: {
    used: number; // 下载已用时间(纳秒)
    speed: number; // 下载速度 (byte/s)
    downloaded: number; // 已下载字节数
  };
  size: number; // 总大小 (byte)
  createdAt: string; // 创建时间(ISO 8601)
  updatedAt: string;
}

任务状态(TaskStatus):

状态含义
ready已创建,等待下载
running下载中
pause已暂停
wait排队等待
error出错
done完成

创建任务 tasks.create ​

ts
const task = await xunlei.tasks.create({
  req: {
    url: 'https://example.com/file.zip',
  },
  opts: {
    name: '示例文件.zip',
  },
});

入参:

ts
interface TaskCreateInput {
  req: Request;
  opts?: Options;
}

interface Request {
  url: string; // 支持 http(s)、磁力链接、电驴链接和本地种子文件路径
  extra?: {
    header?: {
      // 请求头(当前支持以下键,运行时名称透传、由宿主适配)
      'User-Agent'?: string;
      Referer?: string;
      Cookie?: string;
      Authorization?: string;
    };
  };
  labels?: Record<string, string>; // 请求标签
}

interface Options {
  name?: string; // 指定保存文件名
  path?: string; // 指定保存路径,未指定时使用宿主默认下载目录
  extra?: {
    connections?: number; // 并发连接数
  };
}

示例:带请求头创建任务(部分下载源需要携带 Referer / Cookie 等请求头):

ts
await xunlei.tasks.create({
  req: {
    url: 'https://example.com/video.mp4',
    extra: {
      header: {
        Referer: 'https://example.com/watch/123',
        Cookie: savedCookie,
      },
    },
  },
  opts: { name: '视频.mp4' },
});

示例:创建磁力 / BT 任务:

ts
await xunlei.tasks.create({
  req: { url: 'magnet:?xt=urn:btih:...' },
});

TIP

创建任务需要 tasks.create 权限。

创建任务组 tasks.createGroup ​

多个任务需要落在同一个下载目录时(如一个剧集的多集、一个 Release 的多个附件),用 createGroup 一次创建任务组:

ts
const group = await xunlei.tasks.createGroup({
  name: 'my-repo-release', // 组目录名,不允许路径分隔符、. 或 ..
  tasks: [
    { req: { url: 'https://example.com/a.zip' } },
    { req: { url: 'https://example.com/b.zip' }, opts: { name: '第二个文件.zip' } },
  ],
  // opts: { path: '/parent-dir' }, // 可选:父目录,未指定时使用宿主默认下载目录
});

入参与 create 的对应关系:

字段说明
name组目录名(单个目录名,不能包含路径分隔符)
tasks子任务列表,每项与 create 的入参结构相同(req + 可选 opts)
opts.path组所在的父目录,可省略

子任务的 opts.path 只支持相对路径(相对组目录);调用 createGroup 也需要声明 tasks.create 权限,无需额外权限。

查询任务列表 tasks.list ​

list 返回任务 ID 列表,需要结合 detail 获取详情:

ts
const result = await xunlei.tasks.list({
  offset: 0,
  limit: 50,
  status: ['running', 'wait'], // 可选:按状态过滤,支持单个或数组
  sort: 'createdAtDesc', // createdAtDesc(默认)| createdAtAsc
});

for (const id of result.ids) {
  const task = await xunlei.tasks.detail({ id });
  xunlei.logger.info(task.name, task.status, task.progress.speed);
}

返回结构:

ts
interface TaskListResult {
  ids: string[]; // 当前页的任务 ID 列表
  total: number; // 符合筛选条件的任务总数
}

查询任务详情 tasks.detail ​

ts
const task = await xunlei.tasks.detail({ id: 'task-id' });

xunlei.logger.info(task.status); // 'running'
xunlei.logger.info(task.progress.speed); // 下载速度
xunlei.logger.info(task.meta.res.files); // 文件列表 [{ name, path, size, ... }]

detail 的返回值可能是普通任务(type: 'single')或任务组(type: 'group'),两者都以 id 标识,通过 type 字段区分:

ts
const result = await xunlei.tasks.detail({ id: resultId });

if (result.type === 'group') {
  // 任务组:result.opts 为 { path, name },result.children 为子任务列表(不嵌套任务组)
  for (const child of result.children) {
    xunlei.logger.info(child.name, child.status);
  }
} else {
  // 普通任务
  xunlei.logger.info(result.progress.speed);
}

渲染任务进度的典型模式(页面轮询):

ts
async function renderTasks() {
  const { ids } = await xunlei.tasks.list({ limit: 20, sort: 'createdAtDesc' });
  const tasks = await Promise.all(ids.map((id) => xunlei.tasks.detail({ id })));
  // 更新页面 UI
}

setInterval(renderTasks, 1000);

修改任务 tasks.update ​

ts
const task = await xunlei.tasks.update({
  id: 'task-id',
  req: {
    labels: { source: 'miniapp', channel: 'github' }, // 替换任务请求的标签(全量覆盖)
  },
});

当前仅支持更新 req.labels;不支持修改文件名 / 下载地址,也不支持通过此 API 暂停 / 恢复任务。修改下载地址或请求头的场景在事件脚本中完成,见钩子事件 · onStart。

删除任务 tasks.delete ​

ts
const result = await xunlei.tasks.delete({
  id: 'task-id',
  deleteFiles: true, // 可选:同时删除已下载的文件,默认 false
});

删除不可恢复;删除不存在的任务返回 { id, deleted: false } 而不报错。

访问任务文件 tasks.file.access ​

获取任务文件的只读临时 HTTP URL,可在页面中直接 fetch 读取内容(如预览已下载的图片、解析已完成的页面文件):

ts
const access = await xunlei.tasks.file.access({
  taskId: 'task-id',
  fileIndex: 0, // 文件下标,对应 task.meta.res.files 的下标
});

const response = await fetch(access.url);
const buffer = await response.arrayBuffer();
  • 返回的 url 是临时地址,不保证长期有效,请即取即用;
  • 获得该 URL 等同于获得该文件内容的只读能力,不包含写入 / 删除能力;
  • 需要 tasks.file.access 权限(不要求同时声明 tasks.detail)。

权限对照 ​

API所需权限
tasks.createtasks.create
tasks.createGrouptasks.create(复用)
tasks.listtasks.list
tasks.detailtasks.detail
tasks.file.accesstasks.file.access
tasks.updatetasks.update
tasks.deletetasks.delete
runtime.blob.*blob

Blob 与数据下载 ​

页面生成的数据(Canvas 导出的图片、拼装的文本、合成的媒体流)没有下载地址,无法直接创建任务。xunlei.runtime.blob 负责把内存数据转换为可下载的临时 URL(需要在 manifest 中声明 blob 权限):

ts
const url: string = await xunlei.runtime.blob.createObjectURL(source, options?);
await xunlei.runtime.blob.revokeObjectURL(url);

Blob 方式(简单场景) ​

ts
// Canvas 导出图片并创建下载任务
const blob = await new Promise<Blob>((resolve, reject) => {
  canvas.toBlob((b) => (b ? resolve(b) : reject(new Error('图片生成失败'))), 'image/jpeg', 0.9);
});

const url = await xunlei.runtime.blob.createObjectURL(blob);
await xunlei.tasks.create({
  req: { url },
  opts: { name: 'cropped.jpg' },
});

非空 Blob 自动使用自身的类型与大小,并支持断点续传(Range)。

Opener 方式(大文件 / 流式场景) ​

数据源是惰性打开的可重复读取流时,使用 opener 函数。典型场景:边从远端拉取边供下载器分片读取:

ts
const url = await xunlei.runtime.blob.createObjectURL(
  async ({ offset, end }) => {
    const response = await fetch(sourceUrl, {
      headers: { Range: `bytes=${offset}-${end >= 0 ? end : ''}` },
    });
    return response.body!;
  },
  {
    contentType: 'video/mp4',
    size: contentLength, // 启用 Range 时必须提供总大小
    range: true, // 声明数据源支持按区间读取
  },
);

opener 启用 Range 后,每次 HTTP 请求或并发分片都可能独立调用 opener,每次调用必须返回新的 ReadableStream。

使用注意 ​

  • 不要在 tasks.create 返回后立即 revokeObjectURL——下载器可能尚未开始读取;撤销后的 URL 禁止新请求,已开始的读取可以完成;
  • 运行环境销毁、微应用停用或数据源长时间空闲后,URL 会被自动回收,不要持久化保存该 URL;
  • runtime.blob 需要 blob 权限,用它创建下载任务仍需 tasks.create。

下一步 ​