Pregunta

Estoy tratando de usar Apache Commons-Net para transferencias de archivos FTP.

El problema es que los archivos están llegando intermitentemente al servidor corrupto. Por 'corrupto' quiero decir que Winrar me dice que un archivo zip tiene un 'final inesperado del archivo'. A veces los archivos están completamente vacíos. He notado que esto sucede más para archivos más grandes (100kb+), sin embargo, también sucede para archivos pequeños (20kb).

Sé con certeza que el archivo ZIP de origen que se carga es válido y solo tiene 243 kb.

No recibo ningún error/excepción del código.

Aquí está el código que se está ejecutando:

    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);
    }

Y algunos registros:

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

Tenga en cuenta que todos estos se completaron en 15 segundos, y todos los archivos resultantes en el servidor están corruptos.

También he probado sin establecer tiempos de tiempo y el problema aún ocurre.

¿Fue útil?

Solución

Commons FTP predeterminado a los tipos de archivos ASCII. Desea establecerlo en binario cuando se trata de datos binarios como un archivo zip.

De http://commons.apache.org/net/api/org/apache/commons/net/ftp/ftpclient.html

La configuración predeterminada para FTPClient es para usar ftp.ascii_file_type, ftp.non_print_text_format, ftp.stream_transfer_mode y ftp.file_structure. Los únicos tipos de archivos directamente compatibles son ftp.ascii_file_type y ftp.binary_file_type.

Desea hacer setFileType (ftp.binary_file_type) antes de enviar el archivo.

Otros consejos

Tuve este problema a pesar de especificar binary file type Así que escribí código para validar el archivo cargado a través de 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();
    }
}

los MD5 Se toma el código de aquí.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top