Introducción
Una colección archivada puede contener imágenes idénticas con nombres diferentes e imágenes diferentes del mismo tamaño. Implementarás la lectura de bytes de un buscador reutilizable de archivos duplicados y guardarás un inventario completo sin modificar los originales.
Debes conocer clases, métodos, listas, excepciones y lectura básica de archivos en Java. Este laboratorio introduce la entrada binaria, el cierre automático de recursos y SHA-256. El adaptador de directorios y el lanzador están preparados; tú implementas el resumen. Es un ejercicio de solo lectura, no una herramienta de limpieza de disco. El autor estima 50 minutos, incluida la lectura y las correcciones habituales.
Leer los bytes, no el nombre
En este paso, compararás un ejemplo normal de imágenes y después implementarás un resumen que lea cada byte exactamente una vez. Abre WebIDE, elige Terminal > New Terminal y utiliza ese terminal integrado durante todo el laboratorio. Cada laboratorio comienza de forma independiente con sus propios archivos.
cd cambia el directorio de trabajo de este terminal. El script preparado run.sh compila src/*.java en un nuevo directorio temporal de compilación y ejecuta la clase Java indicada. El compilador usa -encoding UTF-8 para que el texto fuente no dependa de la configuración regional del terminal. Solo el compilador y los procesos Java hijos usan C.UTF-8; el proceso Java también utiliza UTF-8 como codificación de texto predeterminada, permitiendo los nombres UTF-8 admitidos sin cambiar la configuración de tu shell.
cd /home/labex/project/duplicates
./run.sh Sample
El auxiliar decodifica los PNG reales y compara sus bytes. Imprime red = renamed: true y red = blue: false. Las tres imágenes tienen las mismas dimensiones y longitud en bytes. Un nombre o una longitud pueden reducir la búsqueda, pero no demuestran igualdad. Opcionalmente, en WebIDE Explorer, expande duplicates/data/gallery y pulsa red.png, blue.png y copies/renamed.png para ver las imágenes. La comparación obligatoria es la decodificación y el resultado de bytes de Sample anteriores. Son imágenes sintéticas originales del curso; no se requieren recursos externos.
La interfaz pública Digester establece un método: digest(Path) devuelve una cadena hexadecimal o lanza IOException si falla la lectura. Path identifica un archivo; un InputStream lee bytes binarios sin interpretarlos como texto. SHA-256 los condensa en una huella candidata de longitud fija. Una unidad posterior seguirá comparando el contenido real antes de declarar duplicados.
Lee el pequeño adaptador y el código inicial antes de sustituirlo:
cat src/Digester.java src/Sha256.java
Se reutiliza un búfer de 1024 bytes. read(buffer) devuelve el número de bytes válidos de esta lectura, o -1 al llegar al final. La última lectura puede llenar solo parte del búfer: los bytes restantes conservan valores anteriores. Por eso sha.update(buffer, 0, count) debe usar la cantidad real. Un archivo vacío no realiza actualizaciones y también tiene un resumen válido.
try (InputStream ... ) es try-with-resources: Java cierra el flujo al terminar el bloque, incluso si la lectura lanza una excepción. El cierre libera el recurso de archivo; no promete una recolección de basura inmediata. MessageDigest.getInstance puede lanzar una excepción comprobada del algoritmo, por lo que el catch exterior la convierte al mismo contrato de errores de operaciones con archivos. La representación hexadecimal usa dos caracteres por byte; & 255 interpreta un byte Java con signo como su valor sin signo.
El siguiente comando escribe la sustitución completa. > reemplaza este archivo fuente. El marcador here-document entre comillas <<'JAVA' envía literalmente a cat el texto intermedio; el JAVA final debe estar solo en su línea, sin sangría. También puedes editar y guardar el mismo archivo en WebIDE. Si, después de pegar, el terminal sigue mostrando un indicador de here-document sin terminar, pulsa Ctrl+C para cancelar y vuelve a pegar el comando completo, incluido el marcador final.
cat > src/Sha256.java <<'JAVA'
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
public class Sha256 implements Digester {
public String digest(Path path) throws IOException {
try {
MessageDigest sha = MessageDigest.getInstance("SHA-256");
byte[] buffer = new byte[1024];
try (InputStream input = Files.newInputStream(path)) {
int count;
while ((count = input.read(buffer)) != -1) {
sha.update(buffer, 0, count);
}
}
StringBuilder hex = new StringBuilder();
for (byte value : sha.digest()) {
hex.append(String.format("%02x", value & 255));
}
return hex.toString();
} catch (NoSuchAlgorithmException error) {
throw new IOException("SHA-256 unavailable", error);
}
}
}
JAVA
Compila y ejecuta de nuevo el mismo ejemplo. La compilación comprueba la nueva clase, mientras que las decisiones de igualdad del ejemplo deben mantenerse.
./run.sh Sample
Si Java indica un número de línea, vuelve a abrir src/Sha256.java, compara las llaves y la ortografía con el contenido de sustitución, guarda y ejecuta de nuevo. Si la ruta es incorrecta, usa pwd para ver el directorio actual y repite el cd absoluto anterior. No hace falta reiniciar la VM.
Guardar cada ruta y su huella
En este paso, utilizarás tu resumen para crear un inventario UTF-8 completo. Continúa en el mismo terminal integrado de WebIDE, en /home/labex/project/duplicates.
El adaptador preparado Inputs.files recorre únicamente el directorio indicado y cierra su flujo de directorio. Rechaza enlaces simbólicos, archivos especiales y rutas con tabuladores o saltos de línea, en lugar de seguirlos u omitirlos silenciosamente. Solo se admiten archivos y directorios ordinarios estables. No cambies las entradas durante el análisis. Las comprobaciones de atributos detectan algunos cambios, pero no congelan el directorio ni garantizan una instantánea concurrente.
El programa preparado Inventory guarda cada ruta relativa, tamaño y resumen cuando todas las lecturas han terminado correctamente. Su formato TSV separa las columnas mediante un tabulador. El adaptador de informes escribe UTF-8 en un nuevo archivo temporal y sustituye la ruta del informe, por lo que ni siquiera un informe existente que sea un enlace duro puede sobrescribir los bytes originales. Esto no garantiza durabilidad ante fallos. La salida debe estar fuera de la raíz analizada. reports ya existe junto a data, evitando que el informe se convierta accidentalmente en su propia entrada. El comando usa Inventory ROOT REPORT: el primer argumento es el directorio que se analiza y el segundo, la ruta del archivo de salida; no los inviertas.
./run.sh Inventory data/gallery reports/inventory.tsv
Espera Inventory complete: 11 files. cat muestra el texto guardado:
cat reports/inventory.tsv
La cabecera es PATH, SIZE, SHA256. Compara las filas de red.png, copies/renamed.png y copies/third.png: distintas rutas comparten un resumen. blue.png tiene el mismo tamaño, pero otro resumen. Ambos archivos vacíos tienen tamaño cero y el mismo resumen válido. tail.bin termina con un búfer parcialmente lleno; su copia coincidente debe seguir obteniendo el mismo resumen. Los archivos con igual nombre north/readme.txt y south/readme.txt mantienen filas separadas.
Practica ahora un error de ruta habitual. El siguiente comando falla intencionadamente con estado de salida 2 y Scan failed; no debe generar un informe nuevo. Un fallo no equivale a un inventario vacío generado correctamente. Un informe anterior no demuestra que la ejecución fallida haya terminado el trabajo.
./run.sh Inventory data/missing reports/missing.tsv
Corrige la ruta de entrada y crea un informe nuevo:
./run.sh Inventory data/gallery reports/recovered.tsv
Los fallos de lectura siguen la misma regla: muestra el error y devuelve un fallo, sin omitir silenciosamente un archivo ilegible. Las comprobaciones ejecutan tu código actual en casos aislados de archivos vacíos, búferes parciales y archivos ilegibles, y leen el inventario real. Todos los originales deben conservar rutas, bytes, permisos, propietario y tiempos de modificación; no los elimines ni sustituyas. Las lecturas ordinarias pueden actualizar los tiempos de acceso. Ya tienes hechos candidatos; en el siguiente laboratorio, las huellas iguales serán candidatas para comparar bytes reales.
Resumen
Has leído archivos binarios con try-with-resources, actualizado SHA-256 con la cantidad realmente leída y guardado un inventario UTF-8 completo. Los nombres, tamaños y resúmenes son hechos candidatos; la comparación de bytes reales confirmará los grupos duplicados.



