Copiar arquivo pela rede MANTENDO A MD5

Olá pessoal… Prontos para a minha primeira pergunta aqui no fórum??? hehehe…

A mais ou menos um ano acompanho vcs off, e TUDO o que eu aprendi de java eu encontrei aqui. Mas agora estou com uma dúvida sobre algo e gostaria de ajuda de vcs, se couber.
Primeiramente vou dizer que meu tópico será grande, então se estiver sem tempo… hehehe…

Bom, estou fazendo um programa para usar em meu serviço (pra facilitar minha vida), que copie um arquivo de uma máquina e mande para outra, via tcp.
Eu achei inúmeros códigos, todos funcionais, todos copiam, mas tem UMA ÚNICA COISA que eu quero e que eles não tem.
Eu preciso copiar um arquivo MANTENDO a MD5 do mesmo. Eu percebi que sempre que eu copiava um arquivo a md5 mudava, pois não era “copiado”, mas sim criado um novo com o mesmo conteúdo. Isso dentro da mesma máquina, ou na mesma rede, eu consegui resolver com o seguinte código:

[code]import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.channels.FileChannel;

public class exemploParaOGuj {

public static void copy(String fOrigem, String fDestino, int numeroPartes) throws FileNotFoundException, IOException {
    FileInputStream origem = new FileInputStream(fOrigem);
    FileOutputStream destino = new FileOutputStream(fDestino);;
    FileChannel fcOrigem = origem.getChannel();
    FileChannel fcDestino = destino.getChannel();
    
    fcOrigem.transferTo(0, fcOrigem.size(), fcDestino);

    destino.close();
    origem.close();

}

}[/code]
Bom, na rede local tá belezinha, O ARQUIVO COPIADO TEM A MD5 IDÊNTICA AO ORIGINAL, o que seria diferente se eu usasse

[code]public void teste() throws FileNotFoundException, IOException{
File file = new File(“teste.txt”);
File fileout = new File(“testeOut.txt”);

    FileInputStream fis = new FileInputStream(file);
    FileOutputStream fos = new FileOutputStream(fileout);
    int dataByteSize = 1024;
    int count = 0;
    byte[] dataBytes = new byte[dataByteSize];
    int nread = 0;
    while ((nread = fis.read(dataBytes)) != -1) {
        fos.write(dataBytes, 0, nread);
    };
}[/code]

Enfim, para a rede local está tudo ok.

Já para a rede em modo servidor, eu achei os seguintes códigos aqui mesmo no guj:

[code]import java.io.;
import java.net.
;

public class Server {

public static void main(String[] args) {
    int port = 5678;
    String outFile = "testeServer.rar";
    String inFile = "I:\\testeCliente.rar";
    try {
        ServerSocket MyService = new ServerSocket(port);
        System.out.println("Aguardando conexão...");
        Socket serviceSocket = MyService.accept();
        File file = new File(outFile);
        DataInputStream input = new DataInputStream(new FileInputStream(file));
        DataOutputStream output = new DataOutputStream(new FileOutputStream(inFile));
        DataInputStream inputMsg = new DataInputStream(serviceSocket.getInputStream());
        DataOutputStream outputMsg = new DataOutputStream(serviceSocket.getOutputStream());
        System.out.println("IP: " + inputMsg.readUTF());
        System.out.println("Nome: " + inputMsg.readUTF());
        byte[] cache = new byte[10240];
        int size = 0;
        while ((size = input.read(cache)) > -1) {
            outputMsg.write(cache, 0, size);
        }
        int readByte = inputMsg.read();
        while (readByte != -1) {
            output.write((byte) readByte);
            readByte = inputMsg.read();
        }
        inputMsg.close();
        outputMsg.close();
        input.close();
        output.close();
        serviceSocket.close();
        MyService.close();
    } catch (Exception e) {
        System.err.println(e.toString());
    }
}

}[/code]
e

[code]import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.net.InetAddress;
import java.net.Socket;
import java.net.UnknownHostException;
import java.nio.channels.SocketChannel;

public class Client {

public static void main(String[] args) {
    int port = 5678;
    InetAddress ia = null;
    try {
        ia = InetAddress.getLocalHost();
    } catch (UnknownHostException e) {
        e.printStackTrace();
    }
    //String IP = ia.getHostAddress();  
    String IP = "localhost";
    String inFile = "D:\\testeServer.rar";
    String outFile = "testeCliente.rar";
    try {
        Socket MyCliente = new Socket(IP, port);
        File file = new File(outFile);
        DataInputStream input = new DataInputStream(new FileInputStream(file));
        DataOutputStream output = new DataOutputStream(new FileOutputStream(inFile));
        DataInputStream inputMsg = new DataInputStream(MyCliente.getInputStream());
        DataOutputStream outputMsg = new DataOutputStream(MyCliente.getOutputStream());
        outputMsg.writeUTF(ia.getHostAddress());
        outputMsg.writeUTF(ia.getHostName());
        int readByte = inputMsg.read();
        while (readByte != -1) {
            output.write((byte) readByte);
            readByte = inputMsg.read();
        }
        byte[] cache = new byte[10240];
        int size = 0;
        while ((size = input.read(cache)) > -1) {
            outputMsg.write(cache, 0, size);
        }
        inputMsg.close();
        outputMsg.close();
        input.close();
        output.close();
        MyCliente.close();
    } catch (Exception e) {
        System.err.println(e.toString());
    }
}

}[/code]

esse código é funcional: COPIA BELEZINHA, mas por ele usar InputStreams ele cria um arquivo novo com o mesmo conteúdo (MUDA A MD5, O QUE EU NÃO QUERO - NÃO POSSO).
Eu estou pensando em pegar o arquivo a ser copiado, colocar dentro de um arquivo ZIP (já tenho pronto), transferir, retirar do arquivo zip e depois verificar a MD5, mas isso demoraria muito, pois ao invés de apenas TRANSFERIR, eu teria que fazer mais dois processos, o de compactação e o de descompactação.

A pergunta é: tem como fazer essa transferência via net (servidor - cliente) sem perder a MD5??? Pois eu acho que vai ser muito GAMBIARRENTO o programa se eu tiver que usar ZIP pra manter minha MD5 (o meu programa usa a MD5 para várias coisas, inclusive pra testar a integridade do arquivo.

Agradeço se alguém tiver alguma dica de pelo menos “onde eu devo procurar”…

Obrigado.

Se está mudando o MD5 do arquivo, ele está sendo corrompido. Não é porque é uma nova InputStream ou sei lá o quê. Não sei se o tal código está “funcionando belezinha” mesmo.

Outra coisa: testar se acabou de copiar os dados, via socket, usando o retorno do read(), é um pouco fria, porque às vezes read() retorna -1 sem que o outro lado tenha terminado. Para os arquivos cujo MD5 deu diferente, veja se não é o tamanho deles que acabou ficando diferente.

Então entanglement, o arquivo foi copiado certinho… Eu sei porque ele abre normalmente, se eu transfiro um ZIP, por exemplo, ele abre de boa.
A questão da MD5 é por causa do Java mesmo, exceto pela cópia usando CHANNEL (EXEMPLO ACIMA), toda vez que eu copio a versão do java muda a MD5, mas o detalhe é que a partir disso, todas as cópias são iguais.

Por exemplo, um programa nosso (uVision) cria um arquivo HEX por MD5 AAA. Ao copiar (sem ser por fileChannel), o java cria o arquivo HEX md5 BBB. A partir disso, TODAS AS CÓPIAS do java tem a mesma MD5, seja copiando novamente o original (AAA) ou a cópia do original (BBB). Mas o arquivo tá íntegro sim… Eu percebi isso por sofrimento mesmo.
O meu problema nem é tanto a cópia, ou a integridade, pois a cópia eu sei que é correta, e a integridade eu poderei colocar em um ZIP, portanto no final o arquivo terá a mesma MD5, o que eu queria mesmo é uma forma menos GAMBIARRENTA de fazer isso, da mesma forma que eu consegui ao utilizar fileChannel ao invés de FileOutputStream.

Mas a idéia de ter que colocar nun zip já está quase dominando… Resta agora esperar pra ver se algum Vini Godoy da vida tem alguma dica do que fazer… A maioria das minhas dúvidas é sempre ele quem dá a resposta (outros tópicos… hehehehe - Desculpe aos demais, mas é o nome dele que eu mais lembro - tem um simu, mas não tenho certeza se é esse o nick…)

Será? Lembre-se que um arquivo .zip não garante a integridade dos dados. Só porque “abre de boa” não quer dizer que não está corrompido.

Embora cada arquivo dentro de um arquivo zip seja conferido com o seu CRC-16 ou 32 (não lembro direito qual), muitas implementações (se não me engano, o visualizador de arquivos .zip do Windows é uma delas) podem optar por simplesmente não lhe avisar se o arquivo estiver realmetne corrompido. Por isso é que eu duvido que ele esteja realmente “igualzinho”.

A única forma de checar isso é pegar o arquivo que foi transferido, copiá-lo de volta de alguma forma confiável (talvez com um pen-drive, quem sabe :slight_smile: ) e comparar binariamente com algum programa - no Windows, pelo Command Prompt, você pode usar o “fc” (use a opção “fc /b” para comparar binariamente os arquivos) e no Linux existe também o “fc”.

Amigo,

Você verificou se o tamanho dos arquivos com md5 diferente estão batendo…
Será que não esta gravando ou deixando de gravar algum byte no final do arquivo… algum byte nulo talvez…
Eu desconheço a tal “mudança do md5”, pelo que eu saiba o md5 é calculado sobre o conteúdo que se for igual será o mesmo! (Exceto alguns casos de colisão que geram o mesmo md5 para conteudos diferentes).

Ou não?

Bom pessoal, descobri o problema, agora tá fácil de resolver… xD

Vou tentar resolver sozinho aqui, assim que eu conseguir eu posto a solução. Se em uma semana eu não conseguir, eu grito aqui…

Mas com relação à MD5, tem uma situação que muda sim… hehehhe… Mas o arquivo NÃO corrompe. Mas enfim, vou tentar me virar, e no fim de semana posto se consegui ou não… xD

[quote=cristhianguedes]Bom pessoal, descobri o problema, agora tá fácil de resolver… xD

Vou tentar resolver sozinho aqui, assim que eu conseguir eu posto a solução. Se em uma semana eu não conseguir, eu grito aqui…

Mas com relação à MD5, tem uma situação que muda sim… hehehhe… Mas o arquivo NÃO corrompe. Mas enfim, vou tentar me virar, e no fim de semana posto se consegui ou não… xD[/quote]

Deixe de ser teimoso - se os arquivos têm MD5 diferente, então eles são diferentes e pronto.

Se corrompeu ou não, depende - alguns formatos toleram que mais bytes sejam escritos no final e o arquivo não fique corrompido (por exemplo, o arquivo original tinha 123.456 bytes e o final tinha 123.460 bytes). Acho que o formato .ZIP é assim, tanto é que ele é usado para criar aqueles executáveis de self-extract.

Mas com certeza se os arquivos têm MD5 diferentes, são diferentes.

(Se o MD5 bater, há uma chance em 2^64 que sejam iguais - 2^-64 = um número com 20 zeros depois da vírgula, ou seja, a probabilidade de dois arquivos diferentes terem o mesmo MD5 (exceto no caso de você ter um MD5 forjado como aqueles casos que alguns pesquisadores conseguiram descobrir) é muito baixa.

[quote=entanglement][quote=cristhianguedes]Bom pessoal, descobri o problema, agora tá fácil de resolver… xD

Vou tentar resolver sozinho aqui, assim que eu conseguir eu posto a solução. Se em uma semana eu não conseguir, eu grito aqui…

Mas com relação à MD5, tem uma situação que muda sim… hehehhe… Mas o arquivo NÃO corrompe. Mas enfim, vou tentar me virar, e no fim de semana posto se consegui ou não… xD[/quote]

Deixe de ser teimoso - se os arquivos têm MD5 diferente, então eles são diferentes e pronto.

Se corrompeu ou não, depende - alguns formatos toleram que mais bytes sejam escritos no final e o arquivo não fique corrompido (por exemplo, o arquivo original tinha 123.456 bytes e o final tinha 123.460 bytes). Acho que o formato .ZIP é assim, tanto é que ele é usado para criar aqueles executáveis de self-extract.

Mas com certeza se os arquivos têm MD5 diferentes, são diferentes.

(Se o MD5 bater, há uma chance em 2^64 que sejam iguais - 2^-64 = um número com 20 zeros depois da vírgula, ou seja, a probabilidade de dois arquivos diferentes terem o mesmo MD5 (exceto no caso de você ter um MD5 forjado como aqueles casos que alguns pesquisadores conseguiram descobrir) é muito baixa.
[/quote]

até pq mudar um dado não significa necessariamente corromper.

vamos pegar pro exemplo um stream RGBA (um png ou bitmap 32bpp).

os pixels q o A = 0, vc pode mudar o os outros 3 bytes (RGB) pra qq valor q não afetará a imagem, já q akele pixel era 100 % transparente.

e md5 não existe no arquivo. nada muda “o MD5”. muda o dado, e como o MD5 é o hash de um determinado dado, ela deixa de ser.

seria como se vc tivesse duas var
a = 3
b = 5

md5 = a + b;

ai vc virar e falar q a continua = 3 e b continua igual = 5, porem o md5 q é a + b não é mais 8.

ou seja.
a + b = 9;

faz sentido?

Bom, não sei o que eu fazia de errado, até hj não consegui descobrir o erro, mas tá aqui a solução do meu problema…

import java.io.*;
import java.net.Socket;
public class LIB_FileUploader {

    private Socket socket;
    private File file;
    public LIB_FileUploader(Socket cliente, File file) throws IOException {
        this.socket = cliente;
        this.file = file;
    }

    public void startSendFile() throws IOException {
        println("|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||");
        println("||||            Inicializando FileSender...                    ||||");
        DataInputStream dis_cli2srv = new DataInputStream(socket.getInputStream());
        DataOutputStream dos_srv2cli = new DataOutputStream(socket.getOutputStream());
        if (file.exists()) {
            String ready = dis_cli2srv.readUTF();
            DataInputStream dis_srv2cli = new DataInputStream(new FileInputStream(file));
            long fileSize = file.length();
            println("|||| -> -> -> -> File name:" + file.getName());
            println("|||| -> -> -> -> File size:" + fileSize);
            dos_srv2cli.writeUTF("" + fileSize);
            println("||||            Inicializando Envio...                         ||||");
            byte[] cache = new byte[1024];
            int size = 0;
            while ((size = dis_srv2cli.read(cache)) > -1) {
                dos_srv2cli.write(cache, 0, size);
            }
            println("||||            Envio finalizado...                            ||||");
        }
        println("|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||");
        dis_cli2srv = null;
        dos_srv2cli = null;
    }

    public void startReceiveFile() throws IOException {
        println("|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||");
        println("||||            Inicializando FileReceiver...                  ||||");
        DataOutputStream dos_received = new DataOutputStream(new FileOutputStream(file.getName()));
        DataInputStream dis_received = new DataInputStream(socket.getInputStream());
        DataOutputStream dos_meToSRV = new DataOutputStream(socket.getOutputStream());
        dos_meToSRV.writeUTF("READY");
        String sSize = dis_received.readUTF();
        Long size = new Long(sSize);
        int readByte = dis_received.read();
        for (long i = 1; i <= size; i++) {
            dos_received.write((byte) readByte);
            if (i < size) {
                readByte = dis_received.read();
            }
        }
        println("||||            recebimento finalizado...                      ||||");
        dos_received.close();
        dos_received = null;
        dis_received = null;
        dos_meToSRV = null;
        println("|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||");
    }

    private void println(String msg) {
        System.out.println(msg);
    }
}

Eu usei isso com:

O SERVER:


import java.io.File;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;

public class Server {

    public static void main(String[] args) throws IOException, Exception {
        ServerSocket Server = new ServerSocket(1234);
        while (true) {
            Socket cliente = Server.accept();
            File fileToSend = new File("teste.txt");
            LIB_FileUploader F = new LIB_FileUploader(cliente, fileToSend);
            F.startSendFile();
        }
    }
}

e com O CLIENT:

import java.io.File;
import java.io.IOException;
import java.net.Socket;
import java.net.UnknownHostException;

public class Client {

    public static void main(String[] args) throws UnknownHostException, IOException {
        //Socket s = new Socket("187.116.175.141", 1234);
        Socket cliente = new Socket("o_ip_do_servidor", 1234);
        System.out.println("Conectado");
        File fileToReceive = new File("teste.txt");
        LIB_FileUploader F = new LIB_FileUploader(cliente, fileToReceive);
        F.startReceiveFile();
        cliente.close();
    }
}

Acho que vou apanhar por estar usando desse jeito, mas tá valendo o aprendizado…