2010-08-10 26 views
5

Próbuję użyć apache commons-net do przesyłania plików ftp.commons-net ftp ładuje uszkodzone pliki

Problem to pliki sporadycznie przybywające na serwer uszkodzony. przez "uszkodzony" mam na myśli to, że winrar mówi mi, że plik zip ma "nieoczekiwany koniec archiwum". czasami pliki są całkowicie puste. Zauważyłem, że dzieje się tak w przypadku większych plików (100kb +), ale dzieje się tak również w przypadku małych plików (20kb).

Wiem, że przesyłany plik zip źródłowy jest prawidłowy i ma tylko 243 kB.

Nie otrzymuję żadnych błędów/wyjątków od kodu.

Oto kod wykonywany:

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

i niektóre dzienniki:

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 

pamiętać, że wszystkie z nich były kompletne w ciągu 15 sekund, a wszystkie wynikające plików na serwerze są uszkodzone .

Przetestowałem również bez ustawiania limitów czasu i problem nadal występuje.

Odpowiedz

13

Commons FTP domyślnie przyjmuje typy plików ASCII. Chcesz ustawić go na Binary podczas pracy z danymi binarnymi, takimi jak plik zip.

Od http://commons.apache.org/net/api/org/apache/commons/net/ftp/FTPClient.html

Domyślne ustawienia FTPClient są na to, aby używać FTP.ASCII_FILE_TYPE, FTP.NON_PRINT_TEXT_FORMAT, FTP.STREAM_TRANSFER_MODE i FTP.FILE_STRUCTURE. Jedynymi obsługiwanymi typami plików są FTP.ASCII_FILE_TYPE i FTP.BINARY_FILE_TYPE.

chcesz ustawić setFileType (FTP.BINARY_FILE_TYPE) przed wysłaniem pliku.

+0

Czy powinienem używać binarnego przy wysyłaniu treści przez 'new ByteArrayInputStream (" ciąg znaków ".getBytes())'? – pstanton

+0

Nie jestem w 100% pewny. Prawdopodobnie jest bezpieczniejszy w ten sposób. Powinien być wystarczająco szybki, aby go przetestować. – PaulJWilliams

+0

Jak to naprawiłeś? mam ten sam problem. – itro

0

miałem ten problem mimo określając binary file type więc napisałem kod do sprawdzania poprawności przesłanego pliku poprzez MD5 mieszaja:

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

Kod MD5 pochodzi z here.