Commons-Net FTP загружает поврежденные файлы
-
27-09-2019 - |
Вопрос
Я пытаюсь использовать 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_mode, ftp.stream_transfer_mode и ftp.file_structure. Единственные типы файлов непосредственно поддерживаются, являются ftp.ascii_file_type и ftp.binare_file_type.
Вы хотите сделать SetFiletype (ftp.binare_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
код взят из здесь.