feat: 支持 Torrent 解析与数据持久化(v1/v2/Hybrid)
- 实现对 Torrent v1、v2 以及 Hybrid 模式的解析
- 完成 Torrent 元信息的数据库持久化逻辑
- 支持 announce 节点、文件信息的提取与存储
- 构建 BtTorrent 表结构与相关插入逻辑
- 预留 OSS 上传接口,明日补充实际上传实现
Change-Id: I4438da0ab364bc8e0d299e1d57474c190584052a
diff --git a/ruoyi-admin/pom.xml b/ruoyi-admin/pom.xml
index fa5532d..4fe1dc0 100644
--- a/ruoyi-admin/pom.xml
+++ b/ruoyi-admin/pom.xml
@@ -17,6 +17,16 @@
<dependencies>
+ <dependency>
+ <groupId>com.fasterxml.jackson.core</groupId>
+ <artifactId>jackson-databind</artifactId>
+ </dependency>
+
+
+
+
+
+
<!-- spring-boot-devtools -->
<dependency>
<groupId>org.springframework.boot</groupId>
@@ -24,6 +34,8 @@
<optional>true</optional> <!-- 表示依赖不会传递 -->
</dependency>
+
+
<!-- spring-doc -->
<dependency>
<groupId>org.springdoc</groupId>
diff --git a/ruoyi-admin/src/main/java/com/ruoyi/torrent/controller/BtTorrentController.java b/ruoyi-admin/src/main/java/com/ruoyi/torrent/controller/BtTorrentController.java
index 2dab95a..6d7626b 100644
--- a/ruoyi-admin/src/main/java/com/ruoyi/torrent/controller/BtTorrentController.java
+++ b/ruoyi-admin/src/main/java/com/ruoyi/torrent/controller/BtTorrentController.java
@@ -1,7 +1,20 @@
package com.ruoyi.torrent.controller;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.Date;
import java.util.List;
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.ruoyi.common.utils.SecurityUtils;
+import com.ruoyi.torrent.domain.BtTorrentAnnounce;
+import com.ruoyi.torrent.domain.BtTorrentFile;
+import com.ruoyi.torrent.service.IBtTorrentAnnounceService;
+import com.ruoyi.torrent.service.IBtTorrentFileService;
import com.ruoyi.torrent.service.IBtTorrentService;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.security.access.prepost.PreAuthorize;
@@ -14,6 +27,9 @@
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.multipart.MultipartFile;
+
import com.ruoyi.common.annotation.Log;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.AjaxResult;
@@ -22,6 +38,11 @@
import com.ruoyi.common.utils.poi.ExcelUtil;
import com.ruoyi.common.core.page.TableDataInfo;
+import java.io.IOException;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.util.UUID;
+
/**
* 种子主Controller
*
@@ -35,6 +56,125 @@
@Autowired
private IBtTorrentService btTorrentService;
+ @Autowired
+ private IBtTorrentFileService btTorrentFileService;
+ @Autowired
+ private IBtTorrentAnnounceService btTorrentAnnounceService;
+
+ private static final String boundary = "----WebKitFormBoundary" + UUID.randomUUID().toString().replace("-", "");
+
+ @PreAuthorize("@ss.hasPermi('system:torrent:add')")
+ @Log(title = "种子主", businessType = BusinessType.INSERT)
+ @PostMapping("/uploadTorrent")
+ public AjaxResult uploadTorrent(@RequestParam("file") MultipartFile file) {
+ try {
+
+ // Create URL connection to Flask server
+ String flaskUrl = "http://localhost:5000/parse_torrent"; // Flask server URL
+ HttpURLConnection connection = (HttpURLConnection) new URL(flaskUrl).openConnection();
+ connection.setRequestMethod("POST");
+ connection.setDoOutput(true);
+ connection.setRequestProperty("Content-Type", "multipart/form-data; boundary=" + boundary);
+
+ // Open output stream
+ try (OutputStream outputStream = connection.getOutputStream()) {
+ // Write the multipart form data boundary and file data
+ writeFormData(outputStream, file);
+
+ // Handle Flask response
+ int responseCode = connection.getResponseCode();
+ if (responseCode != 200) {
+ return AjaxResult.error("Failed to communicate with Flask server, response code: " + responseCode);
+ }
+
+ // Assuming the Flask server responds with JSON, parse the response
+ String responseBody = new String(connection.getInputStream().readAllBytes(), StandardCharsets.UTF_8);
+ parseTorrentData(responseBody);
+ return AjaxResult.success("Torrent uploaded and processed successfully");
+ }
+ } catch (IOException e) {
+ e.printStackTrace();
+ return AjaxResult.error("Error during file upload or communication with Flask service");
+ }
+ }
+
+ // Write multipart form data to the output stream
+ private void writeFormData(OutputStream outputStream, MultipartFile file) throws IOException {
+ // Write the start boundary
+ outputStream.write(("--" + boundary + "\r\n").getBytes(StandardCharsets.UTF_8));
+
+ // Write content-disposition header
+ outputStream.write(("Content-Disposition: form-data; name=\"torrent\"; filename=\"" + file.getOriginalFilename() + "\"\r\n").getBytes(StandardCharsets.UTF_8));
+ outputStream.write(("Content-Type: application/octet-stream\r\n\r\n").getBytes(StandardCharsets.UTF_8));
+
+ // Write the file content
+ file.getInputStream().transferTo(outputStream);
+ outputStream.write("\r\n".getBytes(StandardCharsets.UTF_8));
+
+ // Write the closing boundary
+ outputStream.write(("--" + boundary + "--\r\n").getBytes(StandardCharsets.UTF_8));
+ }
+
+
+ public void parseTorrentData(String responseData) {
+ try {
+
+ // Create ObjectMapper to handle JSON parsing
+ ObjectMapper objectMapper = new ObjectMapper();
+
+ // Parse the JSON response
+ JsonNode jsonNode = objectMapper.readTree(responseData);
+ JsonNode btTorrentNode = jsonNode.get("btTorrent");
+ JsonNode btTorrentAnnounceNode = jsonNode.get("btTorrentAnnounce");
+ JsonNode btTorrentFilesNode = jsonNode.get("btTorrentFiles");
+
+
+ // Convert btTorrentNode to BtTorrent object
+ BtTorrent btTorrent = objectMapper.readValue(btTorrentNode.toString(), BtTorrent.class);
+ btTorrent.setCreatedBy(SecurityUtils.getUsername());
+ btTorrent.setUploaderId(SecurityUtils.getUserId());
+ btTorrentService.insertBtTorrent(btTorrent);
+ Long torrentId=btTorrent.getTorrentId();
+
+ // Convert btTorrentFilesNode to List<BtTorrentFile> using TypeReference
+ List<BtTorrentFile> btTorrentFiles = objectMapper.readValue(
+ btTorrentFilesNode.toString(),
+ new TypeReference<List<BtTorrentFile>>() {});
+ btTorrentFiles.forEach(btFile -> {
+ btFile.setTorrentId(torrentId);
+ btTorrentFileService.insertBtTorrentFile(btFile);
+ }
+ );
+
+ // Convert btTorrentAnnounceNode to List<BtTorrentAnnounce>
+ List<BtTorrentAnnounce> btTorrentAnnounceList = new ArrayList<>();
+ if (btTorrentAnnounceNode.isArray()) {
+ for (JsonNode announceNode : btTorrentAnnounceNode) {
+ // You may need to adjust this depending on your BtTorrentAnnounce class structure
+ BtTorrentAnnounce announce = objectMapper.readValue(announceNode.toString(), BtTorrentAnnounce.class);
+ btTorrentAnnounceList.add(announce);
+ }
+ }
+ btTorrentAnnounceList.forEach(btTorrentAnnounce -> {
+ btTorrentAnnounce.setTorrentId(torrentId);
+ btTorrentAnnounceService.insertBtTorrentAnnounce(btTorrentAnnounce);
+ }
+ );
+
+
+
+
+
+
+
+
+ } catch (Exception e) {
+ e.printStackTrace();
+ // Handle the error (e.g., return null or an error message)
+
+ }
+ }
+
/**
* 查询种子主列表
*/
diff --git a/ruoyi-admin/src/main/java/com/ruoyi/torrent/domain/BtTorrent.java b/ruoyi-admin/src/main/java/com/ruoyi/torrent/domain/BtTorrent.java
index 9a85f2f..30fe780 100644
--- a/ruoyi-admin/src/main/java/com/ruoyi/torrent/domain/BtTorrent.java
+++ b/ruoyi-admin/src/main/java/com/ruoyi/torrent/domain/BtTorrent.java
@@ -2,6 +2,7 @@
import java.util.Date;
import com.fasterxml.jackson.annotation.JsonFormat;
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import com.ruoyi.common.annotation.Excel;
@@ -13,6 +14,7 @@
* @author ruoyi
* @date 2025-04-21
*/
+@JsonIgnoreProperties(ignoreUnknown = true)
public class BtTorrent extends BaseEntity
{
private static final long serialVersionUID = 1L;
diff --git a/ruoyi-admin/src/main/java/com/ruoyi/torrent/domain/BtTorrentAnnounce.java b/ruoyi-admin/src/main/java/com/ruoyi/torrent/domain/BtTorrentAnnounce.java
index 967cc5d..9736a4f 100644
--- a/ruoyi-admin/src/main/java/com/ruoyi/torrent/domain/BtTorrentAnnounce.java
+++ b/ruoyi-admin/src/main/java/com/ruoyi/torrent/domain/BtTorrentAnnounce.java
@@ -1,5 +1,6 @@
package com.ruoyi.torrent.domain;
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import com.ruoyi.common.annotation.Excel;
@@ -11,6 +12,7 @@
* @author ruoyi
* @date 2025-04-21
*/
+@JsonIgnoreProperties(ignoreUnknown = true)
public class BtTorrentAnnounce extends BaseEntity
{
private static final long serialVersionUID = 1L;
diff --git a/ruoyi-admin/src/main/java/com/ruoyi/torrent/domain/BtTorrentFile.java b/ruoyi-admin/src/main/java/com/ruoyi/torrent/domain/BtTorrentFile.java
index 920d871..485b8dd 100644
--- a/ruoyi-admin/src/main/java/com/ruoyi/torrent/domain/BtTorrentFile.java
+++ b/ruoyi-admin/src/main/java/com/ruoyi/torrent/domain/BtTorrentFile.java
@@ -1,5 +1,6 @@
package com.ruoyi.torrent.domain;
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import com.ruoyi.common.annotation.Excel;
@@ -11,6 +12,7 @@
* @author ruoyi
* @date 2025-04-21
*/
+@JsonIgnoreProperties(ignoreUnknown = true)
public class BtTorrentFile extends BaseEntity
{
private static final long serialVersionUID = 1L;
diff --git a/ruoyi-admin/src/main/java/com/ruoyi/torrent/domain/BtTorrentTags.java b/ruoyi-admin/src/main/java/com/ruoyi/torrent/domain/BtTorrentTags.java
index 8f19b72..ccb81ea 100644
--- a/ruoyi-admin/src/main/java/com/ruoyi/torrent/domain/BtTorrentTags.java
+++ b/ruoyi-admin/src/main/java/com/ruoyi/torrent/domain/BtTorrentTags.java
@@ -1,5 +1,6 @@
package com.ruoyi.torrent.domain;
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import com.ruoyi.common.annotation.Excel;
@@ -11,6 +12,7 @@
* @author ruoyi
* @date 2025-04-21
*/
+@JsonIgnoreProperties(ignoreUnknown = true)
public class BtTorrentTags extends BaseEntity
{
private static final long serialVersionUID = 1L;