feat:把bt服务单独拿出来

master
汤全昆 2025-08-22 11:05:25 +08:00
parent a4f2096c5d
commit c3c8f11939
12 changed files with 744 additions and 0 deletions

152
torrent-be/pom.xml 100644
View File

@ -0,0 +1,152 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.0</version>
<relativePath/>
</parent>
<groupId>com.unisinsight</groupId>
<artifactId>torrent-be</artifactId>
<version>1.0.0</version>
<packaging>jar</packaging>
<name>torrent-be</name>
<description>bt服务</description>
<properties>
<java.version>1.8</java.version>
<protobuf.version>3.23.4</protobuf.version>
</properties>
<dependencies>
<!-- Spring Boot Web Starter -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Spring Boot Configuration Processor (可选) -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.18.0</version>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.8.24</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>2.0.49</version>
</dependency>
<dependency>
<groupId>com.dampcake</groupId>
<artifactId>bencode</artifactId>
<version>1.4</version>
</dependency>
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>knife4j-spring-boot-starter</artifactId>
<version>3.0.3</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<scope>provided</scope>
</dependency>
<!-- Apache HttpClient 依赖 -->
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.14</version>
</dependency>
<dependency>
<groupId>com.turn</groupId>
<artifactId>ttorrent-core</artifactId>
<version>1.5</version>
</dependency>
<dependency>
<groupId>com.google.protobuf</groupId>
<artifactId>protobuf-java</artifactId>
<version>${protobuf.version}</version>
</dependency>
<!-- grpc server和spring-boot集成框架 -->
<dependency>
<groupId>net.devh</groupId>
<artifactId>grpc-server-spring-boot-starter</artifactId>
<version>2.14.0.RELEASE</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<extensions>
<extension>
<groupId>kr.motd.maven</groupId>
<artifactId>os-maven-plugin</artifactId>
<version>1.6.2</version>
</extension>
</extensions>
<plugins>
<plugin>
<groupId>org.xolstice.maven.plugins</groupId>
<artifactId>protobuf-maven-plugin</artifactId>
<version>0.6.1</version>
<configuration>
<protocArtifact>com.google.protobuf:protoc:3.19.2:exe:${os.detected.classifier}</protocArtifact>
<pluginId>grpc-java</pluginId>
<pluginArtifact>io.grpc:protoc-gen-grpc-java:1.50.0:exe:${os.detected.classifier}</pluginArtifact>
<!-- proto文件目录 -->
<protoSourceRoot>${project.basedir}/src/main/java/com/unisinsight/project/grpc/proto</protoSourceRoot>
<!-- 生成的Java文件目录,这里指定到这一级就可以咯proto文件有基于package指定 -->
<outputDirectory>${project.basedir}/src/main/java</outputDirectory>
<clearOutputDirectory>false</clearOutputDirectory>
</configuration>
<executions>
<execution>
<goals>
<goal>compile</goal>
<goal>compile-custom</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>

View File

@ -0,0 +1,15 @@
package com.unisinsight.torrent;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
*
*/
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}

View File

@ -0,0 +1,60 @@
package com.unisinsight.torrent.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import springfox.bean.validators.configuration.BeanValidatorPluginsConfiguration;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.Contact;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
@Configuration
@EnableSwagger2
@Import(BeanValidatorPluginsConfiguration.class)
@EnableWebMvc
public class Knife4jConfig implements WebMvcConfigurer {
@Bean
public Docket defaultApi() {
return new Docket(DocumentationType.SWAGGER_2)
.apiInfo(apiInfo())
.groupName("默认分组")
.select()
.apis(RequestHandlerSelectors.basePackage("com.unisinsight.torrent.controller")) // 修改为你的controller包路径
.paths(PathSelectors.any())
.build();
}
private ApiInfo apiInfo() {
return new ApiInfoBuilder()
.title("种子服务API")
.description("# 制作服务接口文档\n\n"
+ "制作种子文件")
.termsOfServiceUrl("http://localhost:8080/")
.contact(new Contact("API开发者", "https://example.com", "developer@example.com"))
.version("1.0.0")
.build();
}
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
// 配置Swagger UI和WebJars资源的访问路径使得这些静态资源可以通过特定的URL路径在Web应用中被访问
registry.addResourceHandler("doc.html")
.addResourceLocations("classpath:/META-INF/resources/");
registry.addResourceHandler("swagger-ui.html")
.addResourceLocations("classpath:/META-INF/resources/");
registry.addResourceHandler("/webjars/**")
.addResourceLocations("classpath:/META-INF/resources/webjars/");
}
}

View File

@ -0,0 +1,25 @@
package com.unisinsight.torrent.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.web.client.RestTemplate;
/**
* @description:
* @author: rdpnr_puzhi
* @create: 2025/08/12
*/
@Configuration
public class RestTemplateConfig {
@Bean
public RestTemplate restTemplate() {
HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory();
factory.setConnectTimeout(600000);
factory.setReadTimeout(600000);
factory.setConnectionRequestTimeout(600000);
return new RestTemplate(factory);
}
}

View File

@ -0,0 +1,67 @@
package com.unisinsight.torrent.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;
/**
* @author : ch
* @version : 1.0
* @ClassName : ThreadConfig
* @Description :
* @DATE : Created in 16:22 2025/8/21
* <pre> Copyright: Copyright(c) 2025 </pre>
* <pre> Company : </pre>
* Modification History:
* Date Author Version Discription
* --------------------------------------------------------------------------
* 2025/08/21 ch 1.0 Why & What is modified: <> *
*/
@Configuration
public class ThreadConfig {
/**
* gRPC线
* /
*/
@Bean("grpcConnectionListenerExecutor")
public ExecutorService grpcConnectionListenerExecutor() {
// 获取系统可用处理器核心数
int corePoolSize =1;
int maximumPoolSize = corePoolSize * 2;
// 设置线程空闲时间
long keepAliveTime = 60L;
// 设置队列大小
int queueCapacity = 1000;
// 创建线程池
return new ThreadPoolExecutor(
// 核心线程数
corePoolSize,
// 最大线程数
maximumPoolSize,
// 空闲线程存活时间
keepAliveTime,
// 时间单位
TimeUnit.SECONDS,
// 任务队列
new LinkedBlockingQueue<>(queueCapacity),
new ThreadFactory() {
private final AtomicInteger threadNumber = new AtomicInteger(1);
@Override
public Thread newThread(Runnable runable) {
Thread thread = new Thread(runable, "grpc-connection-listener-" + threadNumber.getAndIncrement());
thread.setDaemon(false); // 设置为非守护线程
return thread;
}
},
// 拒绝策略:由调用线程执行任务
new ThreadPoolExecutor.CallerRunsPolicy()
);
}
}

View File

@ -0,0 +1,14 @@
package com.unisinsight.torrent.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
public void addResourceHandlers(ResourceHandlerRegistry registry) {
WebMvcConfigurer.super.addResourceHandlers(registry);
registry.addResourceHandler("/api/vdi/file/down/**").addResourceLocations("file:/var/lib/vdi/test/");
}
}

View File

@ -0,0 +1,37 @@
package com.unisinsight.torrent.controller;
import com.unisinsight.torrent.util.BtTorrentUtils;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@RestController
@Slf4j
@RequestMapping("/vdi")
@Api("种子服务")
public class TestController {
@GetMapping("/start")
@ApiOperation(value = "开始做种")
public boolean start(@RequestParam("sourceFile") String sourceFile,
@RequestParam("torrentFile") String torrentFile) {
System.out.println("开始做种...");
boolean seedResult = BtTorrentUtils.createAndSeed(sourceFile, torrentFile);
System.out.println("做种结果: " + (seedResult ? "成功" : "失败"));
return seedResult;
}
@GetMapping("/stop")
@ApiOperation(value = "停止做种")
public boolean stop(@RequestParam("sourceFile") String sourceFile) {
// 测试停止
System.out.println("停止做种...");
boolean stopResult = BtTorrentUtils.stopSeeding(sourceFile);
System.out.println("停止结果: " + (stopResult ? "成功" : "失败"));
return stopResult;
}
}

View File

@ -0,0 +1,172 @@
package com.unisinsight.torrent.exception;
/**
* @author zhangmengfei [yf_zhang.mengfei@unisinsight.com]
* @description
* @date 2020/8/10 19:53
* @since 1.0
*/
public enum BaseErrorCode implements ErrorCode {
INTERNAL_EXCEPTION("00001", "系统内部异常"),
INIT_EXCEPTION("00002", "系统初始化异常"),
UNKNOWN_ERROR("00003", "未知错误"),
MEMORY_ALLOCA_ERROR("00004", "内存分配失败"),
OUT_OF_MEMORY("00005", "内存溢出"),
REQUEST_TIMEOUT("00006", "请求超时"),
ILLEGAL_HANDLE("00007", "非法句柄"),
ENVIRONMENT_GET_FAIL("00008", "环境变量获取失败"),
FREQUENT_SYSTEM("00009", "系统繁忙"),
ILLEGAL_DATASOURCE("00010", "数据源异常"),
FREQUENT_OPERATION("00011", "当前操作频繁"),
UNKNOWN_REQUEST("00012", "未知的请求源"),
REQUEST_EXCEEDED("00013", "请求超过次数限制"),
CONFIG_GET_ERROR("00014", "获取配置错误"),
CONFIG_SET_ERROR("00015", "设置配置错误"),
PROGRAM_NEED_RESTART("00016", "需要重启程序"),
SYSTEM_NEED_RESTART("00017", "需要重启系统"),
CHARACTERISTICS_NOT_SUPPORT("00018", "特性不支持"),
VERIFY_FAILURE("00019", "验证失败"),
CALL_FAILURE("00020", "调用失败"),
MESSAGE_COMPONENT_EXCEPTION("00021", "消息组件异常"),
INSUFFICIENT_STORAGE_CAPACITY("00022", "存储容量不足"),
STORAGE_SERVICE_ERROR("00023", "存储服务错误"),
NETWORK_ERROR("00024", "网络错误"),
IP_ILLEGAL_ADDRESS("00025", "非法IP错误"),
IP_FORBIDDEN("00026", "被禁止的IP"),
IP_RESOLVE_DOMAIN("00027", "解析域名获取IP错误"),
PORT_ERROR("00028", "端口错误"),
API_DOMAIN_ERROR("00029", "域名错误"),
IP_ACCESS_EXCEEDED("00030", "当前IP请求超过限制"),
IP_NULL_ERROR("00031", "IP地址不能为空"),
CONNECTION_FAILURE("00032", "连接失败"),
DATA_SEND_FAILURE("00033", "数据发送失败"),
DATA_RECE_FAILURE("00034", "数据接收失败"),
HTTP_CLIENT_INIT_FAILURE("00035", "HTTP客户端初始化失败"),
HTTP_REQUEST_FAILURE("00036", "HTTP请求失败"),
HTTP_REQUEST_TIME_OUT("00037", "HTTP请求超时"),
HTTP_ERROR_CODE_400("00038", "HTTP错误码400"),
HTTP_ERROR_CODE_404("00039", "HTTP错误码404"),
HTTP_ERROR_CODE_500("500", "HTTP错误码500"),
KAFKA_TOPICE_INVALID("00041", "kafka的topic无效"),
KAFKA_MESSAGE_SEND_FAILURE("00042", " kafka消息发送失败"),
DATABASE_ERROR("00043", "数据库错误"),
DATABASE_OPERATION_FAILED("00044", "数据库操作失败"),
QUERIER_EMPTY_RESULT("00045", "查询无结果"),
ID_REQUIRE("00046", "ID不能为空"),
DATA_DUPLICATED("00047", "数据重复"),
DATA_IN_UPDATING("00048", "数据更新中"),
QUERY_RESULT_NOT_UNIQURE("00049", "查询结果不唯一"),
DICTIONARY_GET_FAILED("00050", "字典获取失败"),
INTERFACE_NOT_EXISTS("00051", "接口不存在"),
PARAMS_CHK_ERROR("00052", "参数校验错误"),
PARAMETERS_INVALID("00053", "无效入参"),
NECESSARY_PARAMS_REQURIED("00054", "必填参数缺失,核对后重试"),
TYPE_NOT_SUPPORTED("00055", "类型不支持"),
NAME_REPEATED("00056", "名称重复"),
PARAMETERS_EMPTY("00057", "参数为空"),
LANGUAGE_NOT_SUPPORT("00058", "不支持该语言"),
API_ABANDONED("00059", "接口停用"),
API_MAINTENANCE("00060", "接口维护"),
API_VER_ERROR("00061", "版本号错误"),
VERIFICATION_CODE_WRONG("00062", "验证码错误"),
FORMAT_NOT_EMPTY("00063", "%s不能为空"),
MOBILE_FORMAT_ERROR("00064", "手机号码格式错误"),
IDCARD_FORMAT_ERROR("00065", "身份证错误"),
EMAIL_FORMAT_ERROR("00066", "邮箱错误"),
LONGITUDE_LATITUDE_ERROR("00067", "经纬度错误"),
REQUEST_PATH_ERROR("00068", "请求路径错误"),
DATA_FORMAT_ERROR("00069", "数据格式错误"),
TIME_PARAMS_ERROR("00070", "时间参数错误"),
CAR_LICENSE_ERROR("00071", "车牌号错误"),
CAMERA_NUMBER_ERROR("00072", "摄像头编号错误"),
CALLBACK_ADDR_ERROR("00073", "回调地址URL错误"),
TIME_FORMAT_ERROR("00074", "时间格式必须是ISO-8601"),
PAGE_NUM_OVERLIMIT("00075", "页码超出限制"),
PAGE_SIZE_OVERLIMIT("00076", "分页大小超出限制"),
THRID_PARTY_INTERFACE_FAILURE("00077", "调用第三方接口失败"),
JSON_PARSE_ERROR("00078", "json解析错误"),
JSON_MESSAGE_FIELD_TYPE_WRONG("00079", "json消息字段类型错误"),
JSON_MESSAGE_FIELD_VALUE_WRONG("00080", "json消息字段取值错误"),
JSON_MESSAGE_FIELD_MISSING("00081", "json消息字段缺失"),
CREATING_JSON_OBJECT_FAILED("00082", "创建json对象失败"),
CREATING_JSON_ARRAY_FAILED("00083", "创建json数组失败"),
JSON_FORMAT_CONTENT_WRONG("00084", "json格式或内容错误"),
FILENAME_ERROR("00085", "文件名错误"),
FILE_ADDRESS_ERROR("00086", "文件地址不存在"),
FILE_ID_NOT_EXIST("00087", "文件ID不存在"),
FILE_ID_LONG("00088", "文件ID过长"),
FILE_TYPE_WRONG("00089", "文件类型错误"),
FILE_CREATE_FAILED("00090", "创建文件失败"),
FILE_OPEN_FAILED("00091", "打开文件失败"),
FILE_DOWNLOAD_FAILED("00092", "下载文件失败"),
FILE_READ_FAILED("00093", "读取文件失败"),
FILE_WRITE_FAILED("00094", "写入文件失败"),
FILE_FORMAT_ERROR("00095", "文件格式错误"),
FILE_ENCRYPTED("00096", "文件被加密"),
EXEL_GENERATE_FAILED("00097", "EXEL生成失败"),
EXEL_IMPORT_FAILED("00098", "EXEL导入失败"),
TEXT_ENCODING_CONVERSION_FAILED("00099", "文本编码转换失败"),
FILE_NOT_RECOGNIZED("00100", "未识别文件"),
FILE_SIZE_EXCEEDS_LIMIT("00101", "文件大小超过限制"),
FILE_UPLOAD_FAILED("00102", "文件上传失败"),
OTHER_USER_UPLOAD_FILE("00103", "其他用户正在上传该文件"),
USER_NOT_FOUND("00104", "用户不存在"),
USER_NOT_LOGIN("00105", "用户未登录"),
USER_NOT_PERMISSON("00106", "用户没权限"),
PASSWORD_ERROR("00107", "密码错误"),
USER_NAME_EXIST("00108", "用户名已存在"),
USER_NAME_WRONG_LENGTH("00109", "用户名长度错误"),
PASSWORD_WRONG_LENGTH("00110", "密码长度错误"),
USER_NAME_OR_PASSWORD_WRONG_FORMAT("00111", "用户名或密码格式错误"),
USER_NAME_OR_PASSWORD_EMPTY("00112", "用户名或密码为空"),
USER_LOGIN_EXPIRED("00113", "用户登录已过期"),
SECURITY_AUTHENTICATION_FAILED("00114", "安全认证失败"),
ACCOUNT_NUMBER_LOCKED("00115", "账号被锁定"),
USER_ADD_FAILED("00116", "添加用户失败"),
USER_DELETE_FAILED("00117", "删除用户失败"),
USER_UPDATE_FAILED("00118", "修改用户失败"),
USER_QUERY_FAILED("00119", "查询用户失败"),
PASSWORD_UPDATE_FAILED("00120", "修改密码失败"),
PASSWORD_NEW_WRONG("00121", "新密码错误"),
PASSWORD_NEW_WRONG_LENGTH("00122", "新密码长度错误"),
PASSWORD_OLD_WRONG_LENGTH("00123", "原密码长度错误"),
PASSWORD_OLD_WRONG("00124", "原密码错误"),
PASSWORD_OLD_NEW_SAME("00125", "新密码和原密码相同"),
USER_NAME_OR_PASSWORD_INCORRECT("00126", "用户名或密码错误,需重新配置"),
DEVICE_GET_FAILED("00127", "设备获取失败"),
CHANNEL_GET_FAILED("00128", "通道获取失败"),
DEVICE_OFFLINE("00129", "设备离线"),
RESOURCES_TREE_GET_FAILED("00130", "资源树获取失败"),
RESOURCES_EXIST("00131", "资源已经存在"),
DEVICE_NOT_EXIST("00132", "设备不存在"),
DEVICE_EXIST("00133", "设备已经存在"),
DEVICE_NUMBER_REACHE_MAX("00134", "设备数达到上限"),
CHANNEL_NUMBER_REACHE_MAX("00135", "设备数达到上限"),
CAMERA_ID_WRONG("00136", "相机ID错误"),
CAMERA_NAME_WRONG("00137", "相机名错误"),
IPC_DEVICE_CODE_EXCEEDS_LONG("00138", "IPC设备编码超长"),
IPC_DEVICE_NAME_EXCEEDS_LONG("00139", "IPC设备名称超长"),
IPC_TYPE_GET_FAILED("00140", "获取IPC款型失败"),
FORMAT_VALIDATE_ERROR("00141", "%s"),
EXPORT_ERROR("00142", "导出EXCEL失败"),
SUCCESS("200", "处理成功");
private String code;
private String message;
BaseErrorCode(String code, String message) {
this.code = code;
this.message = message;
}
@Override
public String getCode() {
return this.code;
}
@Override
public String getMessage() {
return this.message;
}
}

View File

@ -0,0 +1,13 @@
package com.unisinsight.torrent.exception;
/**
* @author zhangmengfei [yf_zhang.mengfei@unisinsight.com]
* @description
* @date 2020/8/10 19:53
* @since 1.0
*/
public interface ErrorCode {
String getCode();
String getMessage();
}

View File

@ -0,0 +1,79 @@
package com.unisinsight.torrent.exception;
import com.fasterxml.jackson.annotation.JsonProperty;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
* @author zhangmengfei [yf_zhang.mengfei@unisinsight.com]
* @description
* @date 2020/8/10 19:53
* @since 1.0
*/
@Data
@ApiModel("返回结果类")
public class Result<T> {
/**
*
*/
@ApiModelProperty("返回码")
@JsonProperty(value = "code")
private String code;
/**
*
*/
@ApiModelProperty("返回信息")
@JsonProperty(value = "message")
private String message;
/**
*
*/
@ApiModelProperty("返回数据")
@JsonProperty(value = "data")
private T data;
public Result() {
}
public Result(String code, String message) {
this.code = code;
this.message = message;
}
public Result(String code, String message, T data) {
this.code = code;
this.message = message;
this.data = data;
}
public static <T> Result<T> successResult(T data) {
return new Result<>(BaseErrorCode.SUCCESS.getCode(), BaseErrorCode.SUCCESS.getMessage(), data);
}
public static <T> Result<T> successResult() {
return new Result<>(BaseErrorCode.SUCCESS.getCode(), BaseErrorCode.SUCCESS.getMessage());
}
public static <T> Result<T> errorResult(ErrorCode errorCode) {
return new Result<>(errorCode.getCode(), errorCode.getMessage());
}
public static <T> Result<T> errorResult(ErrorCode errorCode, T data) {
return new Result<>(errorCode.getCode(), errorCode.getMessage(), data);
}
public static <T> Result<T> errorResultMessage(ErrorCode errorCode, String message) {
return new Result<>(errorCode.getCode(), message);
}
/**
*
*/
public boolean success() {
return BaseErrorCode.SUCCESS.getCode().equals(this.code);
}
}

View File

@ -0,0 +1,96 @@
package com.unisinsight.torrent.util;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
/**
*
*/
public class BtTorrentUtils {
private static final String BT_SCRIPT_PATH = "/var/lib/vdi/nodejs/bttorrent.sh";
private static final Long WAIT_START_TIME = 8000L;
/**
*
*
* @param sourceFile /data/file.iso
* @param torrentFile /data/file.torrent
* @return
*/
public static boolean createAndSeed(String sourceFile, String torrentFile) {
// 验证文件是否存在
if (!new File(sourceFile).exists()) {
System.err.println("源文件不存在: " + sourceFile);
return false;
}
// 确保目标目录存在
new File(torrentFile).getParentFile().mkdirs();
return executeCommand("start", sourceFile, torrentFile);
}
/**
*
*
* @param sourceFile /data/file.iso
* @return
*/
public static boolean stopSeeding(String sourceFile) {
return executeCommand("stop_path", sourceFile, null);
}
/**
* bttorrent.sh
*/
private static boolean executeCommand(String command, String arg1, String arg2) {
try {
// 构造命令
ProcessBuilder pb = new ProcessBuilder(
"bash",
BT_SCRIPT_PATH,
command,
arg1,
arg2 != null ? arg2 : "" // 处理可选参数
).redirectErrorStream(true);
// 启动进程
Process process = pb.start();
// 启动新线程读取输出,避免阻塞
new Thread(() -> {
try {
logProcessOutput(process);
} catch (IOException e) {
e.printStackTrace();
}
}).start();
// 对于start命令只要进程启动成功就返回true
if ("start".equals(command)) {
// 稍微等待一下看初始输出是否有错误
Thread.sleep(WAIT_START_TIME);
return process.isAlive(); // 如果进程还在运行,认为启动成功
}
// 对于stop命令仍然等待完成
return process.waitFor() == 0;
} catch (IOException | InterruptedException e) {
e.printStackTrace();
return false;
}
}
/**
*
*/
private static void logProcessOutput(Process process) throws IOException {
try (BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()))) {
String line;
while ((line = reader.readLine()) != null) {
System.out.println("[BT输出] " + line);
}
}
}
}

View File

@ -0,0 +1,14 @@
# application.yml
server:
port: 8114
spring:
servlet:
multipart:
max-file-size: 25MB
max-request-size: 25MB
knife4j:
production: false
basic:
enable: false