如何在Java中将FTP服务器上的文件复制到同一服务器上的目录?

14
我正在使用Apache Commons FTP上传文件。在上传之前,我想检查服务器上是否已存在该文件,并将其备份到同一服务器上的备份目录中。
有人知道如何将文件从FTP服务器复制到同一服务器上的备份目录吗?
public static void uploadWithCommonsFTP(File fileToBeUpload){
    FTPClient f = new FTPClient();
    FTPFile backupDirectory;
    try {
        f.connect(server.getServer());
        f.login(server.getUsername(), server.getPassword());
        FTPFile[] directories = f.listDirectories();
        FTPFile[] files = f.listFiles();
        for(FTPFile file:directories){
            if (!file.getName().equalsIgnoreCase("backup")) {
                backupDirectory=file;
            } else {
               f.makeDirectory("backup");
            }
        }
        for(FTPFile file: files){
            if(file.getName().equals(fileToBeUpload.getName())){
                //copy file to backupDirectory
            }
        }

    } catch (IOException e) {
        e.printStackTrace();
    }

}

编辑后的代码:仍然存在问题,当我备份zip文件时,备份的文件是损坏的。

有人知道原因吗?

 public static void backupUploadWithCommonsFTP(File fileToBeUpload) {
    FTPClient f = new FTPClient();
    boolean backupDirectoryExist = false;
    boolean fileToBeUploadExist = false;
    FTPFile backupDirectory = null;
    try {
        f.connect(server.getServer());
        f.login(server.getUsername(), server.getPassword());
        FTPFile[] directories = f.listDirectories();
        // Check for existence of backup directory
        for (FTPFile file : directories) {
            String filename = file.getName();
            if (file.isDirectory() && filename.equalsIgnoreCase("backup")) {
                backupDirectory = file;
                backupDirectoryExist = true;
                break;
            }
        }
        if (!backupDirectoryExist) {
            f.makeDirectory("backup");
        }
        // Check if file already exist on the server
        f.changeWorkingDirectory("files");
        FTPFile[] files = f.listFiles();
        f.changeWorkingDirectory("backup");
        String filePathToBeBackup="/home/user/backup/";
        String prefix;
        String suffix;
        String fileNameToBeBackup;
        FTPFile fileReadyForBackup = null;
        f.setFileType(FTP.BINARY_FILE_TYPE);
        f.setFileTransferMode(FTP.BINARY_FILE_TYPE);
        for (FTPFile file : files) {
            if (file.isFile() && file.getName().equals(fileToBeUpload.getName())) {
                prefix = FilenameUtils.getBaseName(file.getName());
                suffix = ".".concat(FilenameUtils.getExtension(file.getName()));
                fileNameToBeBackup = prefix.concat(Calendar.getInstance().getTime().toString().concat(suffix));
                filePathToBeBackup = filePathToBeBackup.concat(fileNameToBeBackup);
                fileReadyForBackup = file;
                fileToBeUploadExist = true;
                break;
            }
        }
        // If file already exist on the server create a backup from it otherwise just upload the file.
        if(fileToBeUploadExist){
            ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
            f.retrieveFile(fileReadyForBackup.getName(), outputStream);
            InputStream is = new ByteArrayInputStream(outputStream.toByteArray());
            if(f.storeUniqueFile(filePathToBeBackup, is)){
                JOptionPane.showMessageDialog(null, "Backup succeeded.");
                f.changeWorkingDirectory("files");
                boolean reply = f.storeFile(fileToBeUpload.getName(), new FileInputStream(fileToBeUpload));
                if(reply){
                    JOptionPane.showMessageDialog(null,"Upload succeeded.");
                }else{
                    JOptionPane.showMessageDialog(null,"Upload failed after backup.");
                }
            }else{
                JOptionPane.showMessageDialog(null,"Backup failed.");
            }
        }else{
            f.changeWorkingDirectory("files");
            f.setFileType(FTP.BINARY_FILE_TYPE);
            f.enterLocalPassiveMode();
            InputStream inputStream = new FileInputStream(fileToBeUpload);
            ByteArrayInputStream in = new ByteArrayInputStream(FileUtils.readFileToByteArray(fileToBeUpload));
            boolean reply = f.storeFile(fileToBeUpload.getName(), in);
            System.out.println("Reply code for storing file to server: " + reply);
            if(!f.completePendingCommand()) {
                f.logout();
                f.disconnect();
                System.err.println("File transfer failed.");
                System.exit(1);
            }
            if(reply){

                JOptionPane.showMessageDialog(null,"File uploaded successfully without making backup." +
                        "\nReason: There wasn't any previous version of this file.");
            }else{
                JOptionPane.showMessageDialog(null,"Upload failed.");
            }
        }
        //Logout and disconnect from server
        in.close();
        f.logout();
        f.disconnect();
    } catch (IOException e) {
        e.printStackTrace();
    }

}

你不明白什么?如果你知道源路径和目标路径,你只需要打开文件并读取(使用缓冲区)然后将其写入目标路径。此外,你也可以使用特定于操作系统的API来复制文件。 - Dmitry Zagorulkin
文件类型是FTPFile。我如何在缓冲区中读写? 你的意思是像FileInputStream in = new FileInputStream(file);这样吗? - itro
http://www.dreamincode.net/forums/topic/32031-ftp-in-java-using-apache-commons-net/ - Dmitry Zagorulkin
你在if块中也尝试过f.setFileType(FTP.BINARY_FILE_TYPE);吗?也许你已经将该文件备份到了目录中。而在else块中,storeFile()的输出是true还是false呢? - RP-
我添加了 f.setFileType(FTP.BINARY_FILE_TYPE); 并且每次将文件放入备份目录时都使用新名称。storeFile 的结果为 true。我在最终编辑后放置了整个代码。 - itro
4个回答

23
如果您正在使用Apache Commons Net的FTPClient,则有一种直接的方法可以将文件从一个位置移动到另一个位置(如果user具有适当的权限)。
ftpClient.rename(from, to);

或者,如果你熟悉ftp命令,你可以使用如下命令:
ftpClient.sendCommand(FTPCommand.yourCommand, args);
if(FTPReply.isPositiveCompletion(ftpClient.getReplyCode())) {
     //command successful;
} else {
     //check for reply code, and take appropriate action.
}

如果您正在使用其他客户端,请查看文档,客户端实现之间不会有太大的变化。
更新:
上述方法将文件移动到“to”目录中,即文件将不再存在于“from”目录中。基本上,ftp协议旨在传输从本地<->远程或远程<->其他远程的文件,而不是在服务器内部进行传输。
解决方法很简单,将完整的文件获取到本地InputStream中,并将其作为新文件写回到备份目录中。
要获取完整的文件,请参见:
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
ftpClient.retrieveFile(fileName, outputStream);
InputStream is = new ByteArrayInputStream(outputStream.toByteArray());

现在,将此流存储到备份目录。首先,我们需要将工作目录更改为备份目录。

// assuming backup directory is with in current working directory
ftpClient.setFileType(FTP.BINARY_FILE_TYPE);//binary files
ftpClient.changeWorkingDirectory("backup");
//this overwrites the existing file
ftpClient.storeFile(fileName, is);
//if you don't want to overwrite it use storeUniqueFile

希望这能帮到你。

1
通过您的解决方案 ftpClient.rename(from, to);,我可以重命名它,但它仍然留在相同的目录中,而不是我期望的备份目录。 我该怎么解决?我如何获取备份路径?如果我想将文件复制到备份目录,第二个参数的正确路径是什么? 我有一个根目录,其中所有文件都位于那里,还有一个备份目录,我在根目录下创建了它。 - itro
1
请注意,以下是关于编程的内容。将以下文本从英语翻译成中文:ftpClient.rename("/root/your_file.txt", "/root/backup/your_file_bak.txt");。这里需要注意的一件事是它是从“from”目录移动到“to”目录。您不会再在“from”目录中找到它。 - RP-
备份 .zip 文件时备份文件损坏,但备份 .csv 文件时备份正常。这是什么原因? - itro
你应该将文件类型设置为“BINARY”,例如ftpClient.setFileType(FTP.BINARY_FILE_TYPE);,我已经更新了答案。 - RP-
我已经设置了,但仍然出现相同的行为。我将更新我的代码,您可以获取整个代码以查看问题所在。 - itro
显示剩余4条评论

1

可以试试这个方法:

我正在使用Apache的库。

ftpClient.rename(from, to)会让操作更简单,我已经在下面的代码中提到了在哪里添加ftpClient.rename(from,to)。

public void goforIt(){


        FTPClient con = null;

        try
        {
            con = new FTPClient();
            con.connect("www.ujudgeit.net");

            if (con.login("ujud3", "Stevejobs27!!!!"))
            {
                con.enterLocalPassiveMode(); // important!
                con.setFileType(FTP.BINARY_FILE_TYPE);
                String data = "/sdcard/prerakm4a.m4a";
                ByteArrayInputStream(data.getBytes());
                FileInputStream in = new FileInputStream(new File(data));
                boolean result = con.storeFile("/Ads/prerakm4a.m4a", in);
                in.close();
                if (result) 
                       {
                            Log.v("upload result", "succeeded");

//$$$$$$$$$$$$$$$$$$$$$$$$$$$$$在这里添加备份$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$//

                   // Now here you can store the file into a backup location

                  // Use ftpClient.rename(from, to) to place it in backup

//$$$$$$$$$$$$$$$$$$$$$$$$$$$$$在这里添加备份$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$//

                       }
                con.logout();
                con.disconnect();
            }
        }
        catch (Exception e)
        {
            e.printStackTrace();
        }   

    }

我应该检查服务器上的文件是否存在,然后备份再上传文件。我认为你在这里做了其他事情。 - itro
仔细再看一遍,如果(result)是在文件成功存储到服务器后将执行的代码块..... - Kumar Vivek Mitra

0

在FTP协议中,没有标准的方法来复制远程文件。尽管如此,一些FTP服务器支持专有或非标准的扩展来实现这个功能。


所以如果你的服务器运行的是带有mod_copy模块的ProFTPD,你可以使用FTP.sendCommand来发出这两个命令:

f.sendCommand("CPFR sourcepath");
f.sendCommand("CPTO targetpath");

第二种可能是您的服务器允许您执行任意 shell 命令。这种情况更不常见。如果您的服务器支持此功能,您可以使用 SITE EXEC 命令:

SITE EXEC cp -p sourcepath targetpath

另一个解决方法是打开第二个连接到FTP服务器,并通过将被动模式数据连接导向主动模式数据连接,使服务器将文件上传到自身。该解决方案的实现(虽然是在PHP中)在FTP copy a file to another place in same FTP中展示。
如果以上方法都不起作用,您只能将文件下载到本地临时位置,然后重新上传到目标位置。这就是@RP-的答案所示

另请参阅在同一FTP中将文件复制到另一个位置的FTP


-1

在同一服务器上进行备份(迁移),你可以使用:

String source="/home/user/some";
String goal  ="/home/user/someOther";
FTPFile[] filesFTP = cliente.listFiles(source);

clientFTP.changeWorkingDirectory(goal);  // IMPORTANT change to final directory

for (FTPFile f : archivosFTP) 
   {
    if(f.isFile())
       {
        cliente.rename(source+"/"+f.getName(), f.getName());
       }
   }

这不是备份/复制文件,而是移动文件。这不是这个问题所涉及的内容。 - Martin Prikryl

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接