22301102 | bc6da0a | 2025-06-02 17:47:29 +0800 | [diff] [blame] | 1 | package com.pt.service; |
| 2 | |
| 3 | import com.pt.entity.TorrentMeta; |
| 4 | import com.pt.repository.TorrentMetaRepository; |
| 5 | import com.pt.utils.BencodeUtils; |
| 6 | import org.springframework.beans.factory.annotation.Autowired; |
| 7 | import org.springframework.stereotype.Service; |
| 8 | |
| 9 | import java.io.*; |
| 10 | import java.security.MessageDigest; |
| 11 | import java.time.LocalDateTime; |
| 12 | import java.util.LinkedHashMap; |
| 13 | import java.util.Map; |
| 14 | |
| 15 | @Service |
| 16 | public class TorrentService { |
| 17 | |
| 18 | // Tracker 服务器的 announce 地址 |
| 19 | private static final String ANNOUNCE_URL = "http://localhost:8080/announce"; |
| 20 | private static final int PIECE_LENGTH = 256 * 1024; |
| 21 | |
| 22 | @Autowired |
| 23 | private TorrentMetaRepository torrentMetaRepository; |
| 24 | |
| 25 | public byte[] generateTorrentBytes(String sourceFilePath) throws Exception { |
| 26 | File sourceFile = new File(sourceFilePath); |
| 27 | |
| 28 | // 构造 info 字典 |
| 29 | Map<String, Object> infoDict = new LinkedHashMap<>(); |
| 30 | infoDict.put("name", sourceFile.getName()); |
| 31 | infoDict.put("length", sourceFile.length()); |
| 32 | infoDict.put("piece length", PIECE_LENGTH); |
| 33 | infoDict.put("pieces", calcPiecesHashes(sourceFile)); |
| 34 | |
| 35 | // 构造完整 torrent 字典 |
| 36 | Map<String, Object> torrentDict = new LinkedHashMap<>(); |
| 37 | torrentDict.put("announce", ANNOUNCE_URL); |
| 38 | torrentDict.put("info", infoDict); |
| 39 | |
| 40 | // 编码成种子数据字节数组 |
| 41 | byte[] bencodedTorrent = BencodeUtils.encode(torrentDict); |
| 42 | |
| 43 | // 计算 info_hash 并保存到数据库(如果需要的话) |
| 44 | MessageDigest sha1 = MessageDigest.getInstance("SHA-1"); |
| 45 | byte[] infoEncoded = BencodeUtils.encode(infoDict); |
| 46 | sha1.update(infoEncoded); |
| 47 | String infoHash = bytesToHex(sha1.digest()); |
| 48 | |
| 49 | TorrentMeta meta = new TorrentMeta(); |
| 50 | meta.setFilename(sourceFile.getName()); |
| 51 | meta.setInfoHash(infoHash); |
| 52 | meta.setSize(sourceFile.length()); |
| 53 | meta.setUploadTime(LocalDateTime.now()); |
| 54 | torrentMetaRepository.save(meta); |
| 55 | |
| 56 | return bencodedTorrent; |
| 57 | } |
| 58 | |
| 59 | |
| 60 | private byte[] calcPiecesHashes(File file) throws Exception { |
| 61 | MessageDigest sha1 = MessageDigest.getInstance("SHA-1"); |
| 62 | try (InputStream fis = new FileInputStream(file)) { |
| 63 | byte[] buffer = new byte[PIECE_LENGTH]; |
| 64 | ByteArrayOutputStream piecesBuffer = new ByteArrayOutputStream(); |
| 65 | |
| 66 | int read; |
| 67 | while ((read = fis.read(buffer)) > 0) { |
| 68 | sha1.reset(); |
| 69 | sha1.update(buffer, 0, read); |
| 70 | piecesBuffer.write(sha1.digest()); |
| 71 | } |
| 72 | return piecesBuffer.toByteArray(); |
| 73 | } |
| 74 | } |
| 75 | |
| 76 | private String bytesToHex(byte[] bytes) { |
| 77 | StringBuilder sb = new StringBuilder(); |
| 78 | for (byte b : bytes) { |
| 79 | sb.append(String.format("%02x", b)); |
| 80 | } |
| 81 | return sb.toString(); |
| 82 | } |
| 83 | } |