Introdução
Uma coleção de arquivos pode conter imagens idênticas com nomes diferentes e imagens diferentes do mesmo tamanho. Você implementará a leitura de bytes de um localizador reutilizável de duplicatas e salvará um inventário completo sem alterar os originais.
Você deve conhecer classes, métodos, listas, exceções e leitura básica de arquivos em Java. Este laboratório apresenta entrada binária, fechamento automático de recursos e SHA-256. O adaptador de diretórios e o inicializador estão preparados; você implementa o hash. É um exercício somente de leitura, não uma ferramenta de limpeza de disco. A estimativa do autor é de 50 minutos, incluindo leitura e correções comuns.
Ler os bytes em vez do nome do arquivo
Nesta etapa, você comparará um exemplo normal de imagens e depois implementará um hash que lê cada byte exatamente uma vez. Abra WebIDE, escolha Terminal > New Terminal e use esse terminal integrado durante todo o laboratório. Cada laboratório começa de forma independente, com seus próprios arquivos.
cd altera o diretório de trabalho deste terminal. O run.sh preparado compila src/*.java em um novo diretório temporário de compilação e executa a classe Java indicada. O compilador usa -encoding UTF-8 para que o texto-fonte não dependa da configuração regional do terminal. Somente o compilador e os processos Java filhos usam C.UTF-8; o processo Java também usa UTF-8 como padrão para texto, permitindo os nomes UTF-8 suportados sem alterar as configurações do seu shell.
cd /home/labex/project/duplicates
./run.sh Sample
O auxiliar decodifica os arquivos PNG reais e compara seus bytes. Ele exibe red = renamed: true e red = blue: false. As três imagens têm as mesmas dimensões e o mesmo comprimento em bytes. Um nome ou comprimento pode restringir a busca, mas não comprova igualdade. Opcionalmente, no WebIDE Explorer, expanda duplicates/data/gallery e clique em red.png, blue.png e copies/renamed.png para visualizar as imagens. A comparação obrigatória é a decodificação e o resultado em bytes de Sample acima. São imagens sintéticas originais do curso; nenhum recurso externo é necessário.
A interface pública Digester define um método: digest(Path) retorna uma string hexadecimal ou lança IOException se a leitura falhar. Path identifica um arquivo; um InputStream lê bytes binários sem interpretá-los como texto. SHA-256 condensa esses bytes em uma impressão digital candidata de tamanho fixo. Uma unidade posterior ainda comparará o conteúdo real antes de declarar duplicatas.
Leia o pequeno adaptador e a implementação inicial antes de substituí-la:
cat src/Digester.java src/Sha256.java
Um buffer de 1024 bytes é reutilizado. read(buffer) retorna a quantidade de bytes válidos desta leitura ou -1 ao chegar ao fim. A última leitura pode preencher apenas parte do buffer; os bytes restantes mantêm valores anteriores. Por isso, sha.update(buffer, 0, count) deve usar a quantidade realmente lida. Um arquivo vazio não realiza atualizações e também tem um hash válido.
try (InputStream ... ) é try-with-resources: o Java fecha o fluxo ao sair do bloco, inclusive quando a leitura lança uma exceção. Fechar libera o recurso de arquivo, mas não promete coleta de lixo imediata. MessageDigest.getInstance pode lançar uma exceção verificada do algoritmo; por isso, o catch externo a converte para o mesmo contrato de erro de operações com arquivos. O hexadecimal usa dois caracteres por byte; & 255 trata um byte Java com sinal como seu valor sem sinal.
O próximo comando escreve a substituição completa. > substitui este arquivo-fonte. O marcador here-document entre aspas <<'JAVA' envia literalmente a cat o texto intermediário; o JAVA final deve ficar sozinho em uma linha, sem recuo. Você também pode editar e salvar o mesmo arquivo no WebIDE. Se, após colar, o terminal continuar no prompt de um here-document incompleto, pressione Ctrl+C para cancelar e cole novamente o comando inteiro, incluindo o 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
Compile e execute novamente o mesmo exemplo. A compilação verifica a nova classe, enquanto as decisões de igualdade do exemplo permanecem iguais.
./run.sh Sample
Se o Java informar um número de linha, reabra src/Sha256.java, compare as chaves e a grafia com a substituição, salve e execute novamente. Se o caminho estiver errado, use pwd para ver o diretório atual e repita o cd absoluto acima. Não é necessário redefinir a VM.
Salvar cada caminho e sua impressão digital
Nesta etapa, você usará seu hash para criar um inventário UTF-8 completo. Continue no mesmo terminal integrado do WebIDE, em /home/labex/project/duplicates.
O adaptador preparado Inputs.files percorre somente o diretório indicado e fecha seu fluxo de diretório. Ele rejeita links simbólicos, arquivos especiais e caminhos com tabulações ou quebras de linha, em vez de segui-los ou ignorá-los silenciosamente. Apenas arquivos e diretórios comuns e estáveis são suportados. Não altere as entradas durante a varredura. As verificações de atributos detectam algumas mudanças, mas não congelam o diretório nem garantem um snapshot com alterações concorrentes.
O Inventory preparado salva cada caminho relativo, tamanho e hash depois que todas as leituras terminam com sucesso. Seu formato TSV separa as colunas por uma tabulação. O adaptador de relatórios grava UTF-8 em um novo arquivo temporário e depois substitui o caminho do relatório. Assim, nem mesmo um relatório existente que seja um link físico pode sobrescrever os bytes originais. Isso não garante durabilidade após um encerramento anormal. A saída deve ficar fora da raiz examinada. reports já existe ao lado de data, evitando que o relatório se torne acidentalmente sua própria entrada. A ordem do comando é Inventory ROOT REPORT: primeiro o diretório a examinar, depois o caminho do arquivo de saída; não os inverta.
./run.sh Inventory data/gallery reports/inventory.tsv
Espere Inventory complete: 11 files. cat exibe o texto salvo:
cat reports/inventory.tsv
O cabeçalho é PATH, SIZE, SHA256. Compare as linhas de red.png, copies/renamed.png e copies/third.png: caminhos diferentes compartilham um hash. blue.png tem o mesmo tamanho, mas outro hash. Ambos os arquivos vazios têm tamanho zero e o mesmo hash válido. A última leitura de tail.bin preenche apenas parte do buffer; sua cópia idêntica ainda precisa receber o mesmo hash. Os arquivos de mesmo nome north/readme.txt e south/readme.txt permanecem em linhas separadas.
Agora pratique um erro de caminho comum. O próximo comando falha intencionalmente com status de saída 2 e Scan failed; ele não deve produzir um novo relatório. Uma falha não equivale a um inventário vazio bem-sucedido. Um relatório anterior não comprova o sucesso desta execução que falhou.
./run.sh Inventory data/missing reports/missing.tsv
Corrija o caminho de entrada e crie um novo relatório:
./run.sh Inventory data/gallery reports/recovered.tsv
Falhas de leitura seguem a mesma regra: mostre o erro e retorne falha, nunca omitindo silenciosamente um arquivo ilegível. As verificações executam seu código atual em arquivos de teste isolados vazios, com buffer final parcial e ilegíveis, e leem o inventário real. Todos os originais devem manter caminhos, bytes, permissões, informações de propriedade e horários de modificação; não os exclua nem substitua. Leituras comuns podem atualizar horários de acesso. Agora você tem fatos candidatos; no próximo laboratório, impressões digitais iguais serão candidatas à comparação dos bytes reais.
Resumo
Você leu arquivos binários com try-with-resources, atualizou SHA-256 com a quantidade realmente lida e salvou um inventário UTF-8 completo. Nomes, tamanhos e hashes são fatos candidatos; a comparação dos bytes reais confirmará os grupos de duplicatas.



