| package com.pt.service; |
| |
| import com.pt.entity.TorrentMeta; |
| import com.pt.repository.TorrentMetaRepository; |
| import com.pt.utils.BencodeUtils; |
| import org.springframework.beans.factory.annotation.Autowired; |
| import org.springframework.stereotype.Service; |
| |
| import java.io.*; |
| import java.security.MessageDigest; |
| import java.time.LocalDateTime; |
| import java.util.LinkedHashMap; |
| import java.util.Map; |
| |
| @Service |
| public class TorrentService { |
| |
| // Tracker 服务器的 announce 地址 |
| private static final String ANNOUNCE_URL = "http://localhost:8080/announce"; |
| private static final int PIECE_LENGTH = 256 * 1024; |
| |
| @Autowired |
| private TorrentMetaRepository torrentMetaRepository; |
| |
| public byte[] generateTorrentBytes(String sourceFilePath) throws Exception { |
| File sourceFile = new File(sourceFilePath); |
| |
| // 构造 info 字典 |
| Map<String, Object> infoDict = new LinkedHashMap<>(); |
| infoDict.put("name", sourceFile.getName()); |
| infoDict.put("length", sourceFile.length()); |
| infoDict.put("piece length", PIECE_LENGTH); |
| infoDict.put("pieces", calcPiecesHashes(sourceFile)); |
| |
| // 构造完整 torrent 字典 |
| Map<String, Object> torrentDict = new LinkedHashMap<>(); |
| torrentDict.put("announce", ANNOUNCE_URL); |
| torrentDict.put("info", infoDict); |
| |
| // 编码成种子数据字节数组 |
| byte[] bencodedTorrent = BencodeUtils.encode(torrentDict); |
| |
| // 计算 info_hash 并保存到数据库(如果需要的话) |
| MessageDigest sha1 = MessageDigest.getInstance("SHA-1"); |
| byte[] infoEncoded = BencodeUtils.encode(infoDict); |
| sha1.update(infoEncoded); |
| String infoHash = bytesToHex(sha1.digest()); |
| |
| TorrentMeta meta = new TorrentMeta(); |
| meta.setFilename(sourceFile.getName()); |
| meta.setInfoHash(infoHash); |
| meta.setSize(sourceFile.length()); |
| meta.setUploadTime(LocalDateTime.now()); |
| torrentMetaRepository.save(meta); |
| |
| return bencodedTorrent; |
| } |
| |
| |
| private byte[] calcPiecesHashes(File file) throws Exception { |
| MessageDigest sha1 = MessageDigest.getInstance("SHA-1"); |
| try (InputStream fis = new FileInputStream(file)) { |
| byte[] buffer = new byte[PIECE_LENGTH]; |
| ByteArrayOutputStream piecesBuffer = new ByteArrayOutputStream(); |
| |
| int read; |
| while ((read = fis.read(buffer)) > 0) { |
| sha1.reset(); |
| sha1.update(buffer, 0, read); |
| piecesBuffer.write(sha1.digest()); |
| } |
| return piecesBuffer.toByteArray(); |
| } |
| } |
| |
| private String bytesToHex(byte[] bytes) { |
| StringBuilder sb = new StringBuilder(); |
| for (byte b : bytes) { |
| sb.append(String.format("%02x", b)); |
| } |
| return sb.toString(); |
| } |
| } |