为磁力虫添加手动上传种子功能

2026-02-20 53 浏览 0 评论

在磁力搜索类应用中,大多数数据来源通常依赖爬虫或第三方站点同步。但在实际使用中, 手动上传 .torrent 种子文件 依然是一个非常实用的补充能力,例如:

  • 私有种子或冷门资源的补充
  • 资源整理与人工校验
  • 管理员或高权限用户维护资源库

本文将以 Egg.js 为后端、Vue + jQuery 为前端,完整实现一个 磁力虫的手动上传种子功能 ,并在上传过程中自动解析种子信息、判断资源类型、去重并入库。


功能目标概览

这个上传功能主要完成以下几件事:

  1. 支持手动选择并上传 .torrent 文件
  2. 后端解析种子文件,提取 infoHash、文件列表、大小等信息
  3. 自动判断资源类型(视频 / 音频 / 文档等)
  4. 对重复 hash 进行校验,避免重复入库
  5. 前端展示上传状态,并在成功后跳转到资源详情页

上传 Controller 实现

后端主要逻辑集中在 upload Controller 中,负责 文件接收、解析、校验与入库

// app/controller/upload.js

'use strict';

const Controller = require('egg').Controller;
const fileType = require('../../extend/fileType');
const { decodeTorrentFile } = require('torrent-parser');

module.exports = class extends Controller {
  async index() {
    await this.ctx.helper.renderView('upload.ejs', { title: '上传种子' });
  }

  async upload() {
    if (this.ctx.request.files && this.ctx.request.files.length) {
      const filepath = this.ctx.request.files[0].filepath;
      const res = decodeTorrentFile(filepath);
      if (!res.name) this.returnError('文件错误');

      const hash = res.infoHash;
      const name = res.name;

1️⃣ 种子解析与基础校验

这里使用了 torrent-parser 来解析 .torrent 文件,核心目的有三个:

  • 获取 infoHash (资源唯一标识)
  • 获取资源名称
  • 判断种子结构是否合法

如果解析结果中没有 name ,基本可以认定是一个无效或损坏的种子文件。


2️⃣ Hash 去重校验

      const hashExists = await this.ctx.service.torrent.hashExists(hash);
      if (hashExists) {
        this.ctx.body = {
          code: 0,
          message: '文件已存在',
          success: false,
          data: hashExists.hash,
        };
        return;
      }

infoHash 是磁力系统中最重要的字段,因此在入库前必须进行唯一性校验。 如果数据库中已经存在该 hash,则直接返回,前端可以据此提示用户。


3️⃣ 计算文件总大小

      let length = 0;
      if (res.files) {
        for (let i = 0; i < res.files.length; i++) {
          length += res.files[i].length;
        }
      } else {
        length = res.length;
      }

这里需要同时兼容:

  • 多文件种子res.files
  • 单文件种子res.length

这一步的结果通常用于排序、筛选以及展示资源体量。


4️⃣ 文件列表过滤与限制

      const files = [];
      if (res.files) {
        for (let i = 0; i < res.files.length; i++) {
          const name = res.files[i].name;
          if (/\.url$/.test(name)) continue;
          if (/\.mht$/.test(name)) continue;
          if (/如果您看到此文件/.test(name)) continue;
          if (/padding_file/.test(name)) continue;
          if (/readme\.txt/i.test(name)) continue;
          // 最多 50 个文件
          if (files.length > 50) break;
          files.push({
            name,
            length: res.files[i].length,
          });
        }
      } else {
        files.push({ name, length: res.length });
      }

这里做了几件非常关键但容易被忽略的事情:

  • 过滤垃圾文件 (广告页、padding 文件、说明文件等)
  • 限制文件数量 ,避免极端种子导致前端或数据库压力
  • 仅保留核心内容文件,提升资源质量

5️⃣ 自动识别资源类型

      // 计算 torrent 类型
      files.sort((a, b) => b.length - a.length);
      let type = 7;
      for (const key in fileType) {
        const ft = fileType[key].values.split(',');
        for (let i = 0; i < ft.length; i++) {
          if (new RegExp(ft[i] + '$', 'i').test(files[0].name)) {
            type = fileType[key].type;
            break;
          }
        }
        if (type !== 7) break;
      }

思路非常直接:

  • 按文件大小排序, 优先使用最大文件判断类型
  • 通过文件后缀匹配预定义的 fileType 映射
  • 默认类型为 7 (通常表示“其他”)

这种策略在实际使用中准确率非常高,尤其适合影视类资源。


6️⃣ 数据入库

      const params = {
        hash,
        name,
        length,
        file_count: files.length,
        download_count: 0,
        files: JSON.stringify(files),
        type,
      };

      await this.ctx.service.torrent.insert(params);

最终将解析后的结果统一写入数据库,为后续搜索、详情页、统计等功能打基础。


上传界面模板实现

前端页面采用 EJS + Vue 的组合,整体结构清晰,逻辑集中在上传流程上。

<!-- // app/view/upload.ejs -->
<!DOCTYPE html>
<html lang="en">
  <%-include('head.ejs')%>
  <body class="footerFixed">
    <%-include('header.ejs')%>

页面结构说明

  • 上传区域 :点击触发文件选择
  • 文件列表表格 :展示文件名、状态、操作
  • 隐藏 input[type=file] :用于真正选择文件

Vue 上传逻辑核心

fileChange(e) {
  this.files = [];
  for (let i = 0; i < e.target.files.length; i++) {
    this.files.push({
      name: e.target.files[i].name,
      file: e.target.files[i],
      status: 0,
      message: '',
      hash: '',
    });
  }

  this.uploading = true;
  for (let i = 0; i < this.files.length; i++) {
    let file = this.files[i].file;
    if (!/torrent/.test(file.name)) {
      toast(file.name + '文件格式错误');
      continue;
    }
    const res = await this.upload(file);
    file.status = res.success ? 1 : 2;
    file.message = res.message || '';
    file.hash = res.data || '';
  }
  this.uploading = false;
}

这里的设计有几个亮点:

  • 支持 多文件连续上传
  • 每个文件有独立状态(待上传 / 成功 / 失败)
  • 上传成功后可直接跳转到资源详情页

总结

通过这一套实现,我们为磁力虫系统补充了一个 稳定、可控、易扩展 的手动上传能力:

  • 后端解析逻辑清晰,容错性强
  • 前端交互简单直观,用户体验友好
  • 数据结构为后续搜索与分类打下基础

发布评论

发布评论前请先 登录
取消
0 评论
点赞
收藏

评论列表 0

暂无评论