práctica 3, falta la última parte

This commit is contained in:
2026-03-04 20:27:45 +01:00
commit aa8b7b30fb
21 changed files with 2705 additions and 0 deletions

View File

@@ -0,0 +1,43 @@
package es.um.redes.nanoFiles.util;
import java.io.File;
import java.util.Map;
/**
* @author rtitos
*
* Utility class acting as database of local files shared by this peer.
*/
public class FileDatabase {
private Map<String, FileInfo> files;
public FileDatabase(String sharedFolder) {
File theDir = new File(sharedFolder);
if (!theDir.exists()) {
theDir.mkdirs();
}
this.files = FileInfo.loadFileMapFromFolder(new File(sharedFolder));
if (files.size() == 0) {
System.err.println("*WARNING: No files found in folder " + sharedFolder);
}
}
public FileInfo[] getFiles() {
FileInfo[] fileinfoarray = new FileInfo[files.size()];
int numFiles = 0;
for (FileInfo f : files.values()) {
fileinfoarray[numFiles++] = f;
}
return fileinfoarray;
}
public String lookupFilePath(String fileHash) {
FileInfo f = files.get(fileHash);
if (f != null) {
return f.filePath;
}
return null;
}
}

View File

@@ -0,0 +1,109 @@
package es.um.redes.nanoFiles.util;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
/**
* @author rtitos
*
* Utility class with static methods to abstract handling of file
* checksums (message digests) to other classes.
*/
public class FileDigest {
/**
* Message digest algorithm used to identify files in nanoP2P.
*/
public static final String algorithm = "SHA-1";
/**
* Get size of digests generated by this class
*
* @return The size (in bytes) of digest, or 0 in case of error.
*/
public static int getFileDigestSize() {
try {
return getDigestSize(algorithm);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
return 0;
}
}
/**
* Get size of digests generated for this algorithm
*
* @param algorithm The desired digest algorithm
* @return The size (in bytes) of its digests
* @throws NoSuchAlgorithmException
*/
private static int getDigestSize(String algorithm) throws NoSuchAlgorithmException {
MessageDigest md = MessageDigest.getInstance(algorithm);
String input = "";
byte[] fileDigest = md.digest(input.getBytes());
return fileDigest.length;
}
/**
* Computes file digest for a given file.
*
* @param filename - the system-dependent file name.
* @return Byte array with resulting file digest.
*/
public static String computeFileChecksumString(String filename) {
return FileDigest.getChecksumHexString(computeFileChecksum(filename));
}
/**
* Computes file digest for a given file.
*
* @param filename - the system-dependent file name.
* @return Byte array with resulting file digest.
*/
private static byte[] computeFileChecksum(String filename) {
MessageDigest md;
try {
md = MessageDigest.getInstance(algorithm);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
return null;
}
InputStream fis;
try {
fis = new FileInputStream(filename);
int numRead;
byte[] buffer = new byte[4096];
do {
numRead = fis.read(buffer);
if (numRead > 0) {
md.update(buffer, 0, numRead);
}
} while (numRead != -1);
fis.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
return null;
} catch (IOException e) {
e.printStackTrace();
return null;
}
return md.digest();
}
private static String getChecksumHexString(byte[] digest) {
// This bytes[] has bytes in decimal format;
// Convert it to hexadecimal format
StringBuilder sb = new StringBuilder();
for (int i = 0; i < digest.length; i++) {
sb.append(Integer.toString((digest[i] & 0xff) + 0x100, 16).substring(1));
}
// return complete hash
return sb.toString();
}
}

View File

@@ -0,0 +1,176 @@
package es.um.redes.nanoFiles.util;
import java.io.File;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Vector;
import es.um.redes.nanoFiles.shell.NFShell;
/**
* @author rtitos
*
* Utility class with static methods to abstract handling of file
* metadata, loading shared failes, search by name substring, etc.
*/
public class FileInfo {
public String fileHash;
public String fileName;
public String filePath;
public long fileSize = -1;
public FileInfo(String hash, String name, long size, String path) {
fileHash = hash;
fileName = name;
fileSize = size;
filePath = path;
}
public FileInfo() {
}
public String toString() {
StringBuffer strBuf = new StringBuffer();
strBuf.append(String.format("%1$-30s", fileName));
strBuf.append(String.format("%1$10s", fileSize));
strBuf.append(String.format(" %1$-45s", fileHash));
return strBuf.toString();
}
public static void printToSysout(FileInfo[] files) {
StringBuffer strBuf = new StringBuffer();
strBuf.append(String.format("%1$-30s", "Name"));
strBuf.append(String.format("%1$10s", "Size"));
strBuf.append(String.format(" %1$-45s", "Hash"));
System.out.println(strBuf);
for (FileInfo file : files) {
System.out.println(file);
}
}
/**
* Scans the given directory and returns an array of FileInfo objects, one for
* each file recursively found in the given folder and its subdirectories.
*
* @param sharedFolderPath The folder to be scanned
* @return An array of file metadata (FileInfo) of all the files found
*/
public static FileInfo[] loadFilesFromFolder(String sharedFolderPath) {
File folder = new File(sharedFolderPath);
Map<String, FileInfo> files = loadFileMapFromFolder(folder);
FileInfo[] fileinfoarray = new FileInfo[files.size()];
Iterator<FileInfo> itr = files.values().iterator();
int numFiles = 0;
while (itr.hasNext()) {
fileinfoarray[numFiles++] = itr.next();
}
return fileinfoarray;
}
/**
* Scans the given directory and returns a map of <filehash,FileInfo> pairs.
*
* @param folder The folder to be scanned
* @return A map of the metadata (FileInfo) of all the files recursively found
* in the given folder and its subdirectories.
*/
protected static Map<String, FileInfo> loadFileMapFromFolder(final File folder) {
Map<String, FileInfo> files = new HashMap<String, FileInfo>();
scanFolderRecursive(folder, files);
return files;
}
private static void scanFolderRecursive(final File folder, Map<String, FileInfo> files) {
if (folder.exists() == false) {
System.err.println("scanFolder cannot find folder " + folder.getPath());
return;
}
if (folder.canRead() == false) {
System.err.println("scanFolder cannot access folder " + folder.getPath());
return;
}
for (final File fileEntry : folder.listFiles()) {
if (fileEntry.isDirectory()) {
scanFolderRecursive(fileEntry, files);
} else {
String fileName = fileEntry.getName();
String filePath = fileEntry.getPath();
String fileHash = FileDigest.computeFileChecksumString(filePath);
long fileSize = fileEntry.length();
if (fileSize > 0) {
files.put(fileHash, new FileInfo(fileHash, fileName, fileSize, filePath));
} else {
if (fileName.equals(NFShell.FILENAME_TEST_SHELL)) {
NFShell.enableVerboseShell();
System.out.println("[Enabling verbose shell]");
} else {
System.out.println("Ignoring empty file found in shared folder: " + filePath);
}
}
}
}
}
public static FileInfo[] lookupFilenameSubstring(FileInfo[] files, String filenameSubstr) {
String needle = filenameSubstr.toLowerCase();
Vector<FileInfo> matchingFiles = new Vector<FileInfo>();
for (int i = 0; i < files.length; i++) {
if (files[i].fileName.toLowerCase().contains(needle)) {
matchingFiles.add(files[i]);
}
}
FileInfo[] result = new FileInfo[matchingFiles.size()];
matchingFiles.toArray(result);
return result;
}
public static FileInfo[] lookupHashSubstring(FileInfo[] files, String hashSubstr) {
String needle = hashSubstr.toLowerCase();
Vector<FileInfo> matchingFiles = new Vector<FileInfo>();
for (int i = 0; i < files.length; i++) {
if (files[i].fileHash.toLowerCase().contains(needle)) {
matchingFiles.add(files[i]);
}
}
FileInfo[] result = new FileInfo[matchingFiles.size()];
matchingFiles.toArray(result);
return result;
}
// Serialización sencilla para enviar listas de FileInfo por TCP
public static byte[] serializeList(FileInfo[] files) throws IOException {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeInt(files.length);
for (FileInfo f : files) {
oos.writeUTF(f.fileHash);
oos.writeUTF(f.fileName);
oos.writeLong(f.fileSize);
}
oos.flush();
return bos.toByteArray();
}
public static FileInfo[] deserializeList(byte[] data) throws IOException {
ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(data));
int n = ois.readInt();
FileInfo[] files = new FileInfo[n];
for (int i = 0; i < n; i++) {
String hash = ois.readUTF();
String name = ois.readUTF();
long size = ois.readLong();
files[i] = new FileInfo(hash, name, size, null);
}
return files;
}
}

View File

@@ -0,0 +1,22 @@
package es.um.redes.nanoFiles.util;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
public class FileNameUtil {
/**
* Devuelve una ruta disponible a partir de un nombre base. Si ya existe,
* añade sufijos .1, .2, etc. hasta encontrar un nombre libre.
*/
public static Path chooseAvailableName(String baseName) {
Path path = Paths.get(baseName);
int suffix = 1;
while (Files.exists(path)) {
path = Paths.get(baseName + "." + suffix);
suffix++;
}
return path;
}
}

View File

@@ -0,0 +1,25 @@
package es.um.redes.nanoFiles.util;
public class NickGenerator {
private static final String[] TEMPLATES = { "jim", "tim", "jay", "sam", "pat", "max", "liz", "ivy", "zoe", "mia",
"bea", "gus", "ted", "ana", "eva", "amy", "leo", "ben", "lou", "joel", "ivan", "otto", "alex", "casey",
"riley", "toby", "felix", "edith", "fran", "simon", "eric", "danny", "roger" };
/**
* Genera un nickname aleatorio base: prefijo de la lista y un dígito.
*/
public static String randomNickname() {
int idx = (int) (Math.random() * TEMPLATES.length);
int digit = (int) (Math.random() * 10);
return TEMPLATES[idx] + digit;
}
/**
* Genera una variante del nickname original añadiendo un único dígito.
*/
public static String variantWithDigit(String base) {
int digit = (int) (Math.random() * 10);
return base + digit;
}
}