سؤال

أحاول استخدام Apache Commons-Net لنقل ملفات FTP.

المشكلة هي أن الملفات تصل بشكل متقطع إلى الخادم فاسد. من خلال "الفاسدة" أعني أن Winrar يخبرني أن ملف zip لديه "نهاية غير متوقعة للأرشيف". في بعض الأحيان تكون الملفات فارغة تمامًا. لقد لاحظت أن هذا يحدث أكثر بالنسبة للملفات الأكبر (100 كيلو بايت+) ، ومع ذلك يحدث للملفات الصغيرة أيضًا (20 كيلو بايت).

أعرف حقيقة أن ملف Source Zip الذي يتم تحميله صالح ، وهو فقط 243 كيلو بايت.

لا أحصل على أي أخطاء/استثناءات من الكود.

إليك الرمز الذي يتم تنفيذه:

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

وبعض السجلات:

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

لاحظ أن كل هذه الأشياء كانت كاملة في غضون 15 ثانية ، وأن جميع الملفات الناتجة على الخادم تالفة.

لقد اختبرت أيضًا دون تعيين أي مهام وما زالت المشكلة تحدث.

هل كانت مفيدة؟

المحلول

Commons FTP الافتراضيات لأنواع ملفات ASCII. تريد ضبطه على ثنائي عند التعامل مع البيانات الثنائية مثل ملف zip.

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

الإعدادات الافتراضية لـ FTPClient هي استخدام ftp.ascii_file_type ، ftp.non_print_text_format ، ftp.stream_transfer_mode ، و ftp.file_structure. أنواع الملفات الوحيدة المدعومة مباشرة هي ftp.ascii_file_type و ftp.binary_file_type.

تريد القيام بـ setFileType (ftp.binary_file_type) قبل إرسال الملف.

نصائح أخرى

واجهت هذه المشكلة على الرغم من تحديدها binary file type لذلك كتبت رمزًا للتحقق من صحة الملف الذي تم تحميله عبر MD5 التجزئة:

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

ال MD5 الكود مأخوذ من هنا.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top