为磁力虫添加手动上传种子功能
在磁力搜索类应用中,大多数数据来源通常依赖爬虫或第三方站点同步。但在实际使用中, 手动上传 .torrent 种子文件 依然是一个非常实用的补充能力,例如:
- 私有种子或冷门资源的补充
- 资源整理与人工校验
- 管理员或高权限用户维护资源库
本文将以 Egg.js 为后端、Vue + jQuery 为前端,完整实现一个 磁力虫的手动上传种子功能 ,并在上传过程中自动解析种子信息、判断资源类型、去重并入库。
功能目标概览
这个上传功能主要完成以下几件事:
- 支持手动选择并上传
.torrent文件 - 后端解析种子文件,提取 infoHash、文件列表、大小等信息
- 自动判断资源类型(视频 / 音频 / 文档等)
- 对重复 hash 进行校验,避免重复入库
- 前端展示上传状态,并在成功后跳转到资源详情页
上传 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;
}这里的设计有几个亮点:
- 支持 多文件连续上传
- 每个文件有独立状态(待上传 / 成功 / 失败)
- 上传成功后可直接跳转到资源详情页
总结
通过这一套实现,我们为磁力虫系统补充了一个 稳定、可控、易扩展 的手动上传能力:
- 后端解析逻辑清晰,容错性强
- 前端交互简单直观,用户体验友好
- 数据结构为后续搜索与分类打下基础




