Pergunta

Estou tentando usar o Apache Commons-Net para transferências de arquivos FTP.

O problema é que os arquivos estão chegando intermitentemente ao servidor corrompido. Por 'corrupto', quero dizer que Winrar me diz que um arquivo zip tem um 'fim inesperado do arquivo'. Às vezes, os arquivos estão completamente vazios. Percebi que isso acontece mais para arquivos maiores (100kb+), no entanto, também acontece para arquivos pequenos (20KB).

Sei que o arquivo ZIP de origem que está sendo carregado é válido e tem apenas 243kb.

Não recebo erros/exceções do código.

Aqui está o código que está sendo executado:

    try
    {
        int CON_TIMEOUT = (int) TimeUnit.SECONDS.toMillis(20); // fail if can't connect within 20 seconds
        int LIVE_TIMEOUT = (int) TimeUnit.MINUTES.toMillis(5); // allow up to 5 minutes for data transfers

        FTPClient client = new FTPClient();
        client.setConnectTimeout(CON_TIMEOUT);
        client.setDataTimeout(LIVE_TIMEOUT);
        client.connect(host);
        client.setSoTimeout(LIVE_TIMEOUT);
        client.login(user, pass);
        client.changeWorkingDirectory(dir);
        log("client ready");

        File file = new File(filePath);
        String name = new Date().getTime() + "-" + file.getName();

        InputStream fis = null;
        try
        {
            fis = new FileInputStream(file);
            if (!client.storeFile(name, fis))
                throw new RuntimeException("store failed");
            log("store " + name + " complete");
        }
        finally
        {
            IOUtils.closeQuietly(fis);
            try
            {
                client.logout();
                log("logout");
            }
            catch (Throwable e)
            {
                log("logout failed", e);
            }
            try
            {
                client.disconnect();
                log("disconnect");
            }
            catch (Throwable e)
            {
                log("disconnect failed", e);
            }
        }
    }
    catch (Throwable e)
    {
        log("transfer failed", e);
    }

E alguns toras:

2010-08-10 21:32:38 client ready
2010-08-10 21:32:49 store 1281439958234-file.zip complete
2010-08-10 21:32:49 logout
2010-08-10 21:32:49 disconnect
2010-08-10 21:32:50 client ready
2010-08-10 21:33:00 store 1281439970968-file.zip complete
2010-08-10 21:33:00 logout
2010-08-10 21:33:00 disconnect
2010-08-10 21:33:02 client ready
2010-08-10 21:33:11 store 1281439982234-file.zip complete
2010-08-10 21:33:11 logout
2010-08-10 21:33:11 disconnect
2010-08-10 21:33:15 client ready
2010-08-10 21:33:25 store 1281439995890-file.zip complete
2010-08-10 21:33:26 logout
2010-08-10 21:33:26 disconnect
2010-08-10 21:33:27 client ready
2010-08-10 21:33:36 store 1281440007531-file.zip complete
2010-08-10 21:33:36 logout
2010-08-10 21:33:36 disconnect
2010-08-10 21:33:37 client ready
2010-08-10 21:33:48 store 1281440017843-file.zip complete
2010-08-10 21:33:48 logout
2010-08-10 21:33:48 disconnect
2010-08-10 21:33:49 client ready
2010-08-10 21:33:59 store 1281440029781-file.zip complete
2010-08-10 21:33:59 logout
2010-08-10 21:33:59 disconnect
2010-08-10 21:34:00 client ready
2010-08-10 21:34:09 store 1281440040812-file.zip complete
2010-08-10 21:34:09 logout
2010-08-10 21:34:09 disconnect
2010-08-10 21:34:10 client ready
2010-08-10 21:34:23 store 1281440050859-file.zip complete
2010-08-10 21:34:24 logout
2010-08-10 21:34:24 disconnect
2010-08-10 21:34:25 client ready
2010-08-10 21:34:35 store 1281440065421-file.zip complete
2010-08-10 21:34:35 logout
2010-08-10 21:34:35 disconnect

Observe que tudo isso foi completo em 15 segundos e todos os arquivos resultantes no servidor estão corrompidos.

Também testei sem definir quaisquer tempo limite e o problema ainda ocorre.

Foi útil?

Solução

O FTP do Commons é o padrão dos tipos de arquivos ASCII. Você deseja defini -lo como binário ao lidar com dados binários, como um arquivo zip.

A partir de http://commons.apache.org/net/api/org/apache/commons/net/ftp/ftpclient.html

As configurações padrão do FTPClient são para que ele use ftp.ascii_file_type, ftp.non_print_text_format, ftp.stream_transfer_mode e ftp.file_structure. Os únicos tipos de arquivo suportados diretamente são ftp.ascii_file_type e ftp.binary_file_type.

Você deseja fazer o setFileType (ftp.binary_file_type) antes de enviar o arquivo.

Outras dicas

Eu tive esse problema, apesar de especificar binary file type Então eu escrevi código para validar o arquivo carregado via MD5 Hashing:

public void upload(String sourceFilePath) throws Exception
{
    while (true)
    {
        // Upload
        File sourceFile = new File(sourceFilePath);
        String sourceFileHash = MD5Checksum.getMD5Checksum(sourceFilePath);
        String remoteFile = sourceFile.getName();

        try (InputStream inputStream = new FileInputStream(sourceFile))
        {
            boolean successful = ftpClient.storeFile(remoteFile, inputStream);

            if (!successful)
            {
                throw new IllegalStateException("Upload of " + sourceFilePath + " failed!");
            }
        }

        // Download
        File temporaryFile = File.createTempFile("prefix", "suffix");
        try (OutputStream outputStream = new BufferedOutputStream(new FileOutputStream(temporaryFile)))
        {
            boolean successful = ftpClient.retrieveFile(remoteFile, outputStream);

            if (!successful)
            {
                throw new IllegalStateException("Download of " + sourceFilePath + " failed!");
            }
        }

        String downloadFileHash = MD5Checksum.getMD5Checksum(temporaryFile.getAbsolutePath());
        Files.delete(temporaryFile.toPath());

        // Make sure the file hashes match
        if (sourceFileHash.equals(downloadFileHash))
        {
            break;
        }
    }
}

MD5Checksum.java:

import java.io.*;
import java.security.MessageDigest;

public class MD5Checksum
{
    private static byte[] createChecksum(String filename) throws Exception
    {
        try (InputStream fileInputStream = new FileInputStream(filename))
        {
            byte[] buffer = new byte[1024];
            MessageDigest complete = MessageDigest.getInstance("MD5");
            int numRead;

            do
            {
                numRead = fileInputStream.read(buffer);
                if (numRead > 0)
                {
                    complete.update(buffer, 0, numRead);
                }
            } while (numRead != -1);

            return complete.digest();
        }
    }

    public static String getMD5Checksum(String filename) throws Exception
    {
        byte[] checksum = createChecksum(filename);
        StringBuilder result = new StringBuilder();

        for (byte singleByte : checksum)
        {
            result.append(Integer.toString((singleByte & 0xff) + 0x100, 16).substring(1));
        }

        return result.toString();
    }
}

o MD5 O código é retirado de aqui.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top