FTP协议实现上传和下载

FTP协议上传下载

tags: java

简介

FTPFile Transfer Protocol(文件传输协议)的英文简称,而中文简称为“文传协议”。用于Internet上的控制文件的双向传输。同时,它也是一个应用程序(Application)。基于不同的操作系统有不同的FTP应用程序,而所有这些应用程序都遵守同一种协议以传输文件。在FTP的使用当中,用户经常遇到两个概念:"下载"(Download)和"上传"(Upload)。"下载"文件就是从远程主机拷贝文件至自己的计算机上;"上传"文件就是将文件从自己的计算机中拷贝至远程主机上.

FTP的传输方式有两种: ASCII传输方式和二进制传输方式

  • ASCII传输方式
    假定用户正在拷贝的文件包含的简单ASCII码文本,如果在远程机器上运行的不是UNIX,当文件传输时ftp通常会自动地调整文件的内容以便于把文件解释成另外那台计算机存储文本文件的格式。
    但是常常有这样的情况,用户正在传输的文件包含的不是文本文件,它们可能是程序,数据库,字处理文件或者压缩文件。在拷贝任何非文本文件之前,用binary 命令告诉ftp逐字拷贝。
  • 二进制传输模式
    在二进制传输中,保存文件的位序,以便原始和拷贝的是逐位一一对应的。即使目的地机器上包含位序列的文件是没意义的。例如,macintosh以二进制方式传送可执行文件到Windows系统,在对方系统上,此文件不能执行。
    如在ASCII方式下传输二进制文件,即使不需要也仍会转译。这会损坏数据。(ASCII方式一般假设每一字符的第一有效位无意义,因为ASCII字符组合不使用它。如果传输二进制文件,所有的位都是重要的。)

FTP支持两种模式: Standard(PORT主动方式)和Passive(PSV被动方式)

  • PORT: FTP 客户端首先和服务器的TCP 21端口建立连接,用来发送命令,客户端需要接收数据的时候在这个通道上发送PORT命令。PORT命令包含了客户端用什么端口接收数据。在传送数据的时候,服务器端通过自己的TCP 20端口连接至客户端的指定端口发送数据。FTP server必须和客户端建立一个新的连接用来传送数据。
  • PSV: 建立控制通道和Standard模式类似,但建立连接后发送Pasv命令。服务器收到Pasv命令后,打开一个临时端口(端口号大于1023小于65535)并且通知客户端在这个端口上传送数据的请求,客户端连接FTP服务器此端口,然后FTP服务器将通过这个端口传送数据。

Apache Commons Net来下载文件的API

org.apache.commons.net.ftp.FTPClient类提供两个方法来从FTP服务器下载文件:

  • boolean retrieveFile(String remote, OutputStream local)
    该方法将远程文件remote写入到指定的本地OutputStream中,返回为true表示下载成功,false下载失败.此方法适合不关心远程文件如何写入到本地磁盘,仅仅是使用给定的OutputStream来写指定的文件.
  • InputStream retrieveFileStream(String remote)
    该方法返回一个从远程文件remote读入的InputStream. 利用此方法我们可以更好的控制如何读写数据,但此方法需要结合completePendingCommand()一起使用,该方法用来完成文件的传输,并检测返回值已验证是否下载完成.

那么该选择何种方法来下载了?

  • 第一种方法提供最简单的文件下载的方式, 只需要将文件的输出流写入本地磁盘即可.
  • 第二种方法需要输出更多的代码,我们需要创建OutputStream来写入读取返回InputStream得到的byte arrays. 但需要知道文件下载的进度的时候,该方法尤为适用.但是要注意调用completePendingCommand()来完成传输和检测结果.
  • 两个方法都会抛出IOException(或者FTPConnectionClosedException and CopyStreamException).

此外, 当使用retrieveFile() and retrieveFileStream()方法时,下面的两个方法必须要调用:

  • void enterLocalPassiveMode():设置PSV模式
  • boolean setFileType(int fileType):设置传输方式,一般选用二进制方式

下载单文件的步骤

  • 连接并登陆服务器
  • 设置Passive模式
  • 设置传输方式为二进制方式
  • 构造远程文件(File remoteF= new File(remote));
  • 构造OutputSream去写文件
  • 下载文件可以选择retriveFile()或者retrieveFileStream()
  • 退出登陆并断开连接

代码示例:

import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
 
import org.apache.commons.net.ftp.FTP;
import org.apache.commons.net.ftp.FTPClient;
 
/**
 * A program demonstrates how to upload files from local computer to a remote
 * FTP server using Apache Commons Net API.
 * @author www.codejava.net
 */
public class FTPDownloadFileDemo {

    public static void main(String[] args) {
        String server = "www.myserver.com";
        int port = 21;
        String user = "user";
        String pass = "pass";
 
        FTPClient ftpClient = new FTPClient();
        try {
            ftpClient.connect(server, port);
            ftpClient.login(user, pass);
            ftpClient.enterLocalPassiveMode();
            ftpClient.setFileType(FTP.BINARY_FILE_TYPE);
 
            // APPROACH #1: using retrieveFile(String, OutputStream)
            String remoteFile1 = "/test/video.mp4";
            File downloadFile1 = new File("D:/Downloads/video.mp4");
            OutputStream outputStream1 = new BufferedOutputStream(new FileOutputStream(downloadFile1));
            boolean success = ftpClient.retrieveFile(remoteFile1, outputStream1);
            outputStream1.close();
 
            if (success) {
                System.out.println("File #1 has been downloaded successfully.");
            }
 
            // APPROACH #2: using InputStream retrieveFileStream(String)
            String remoteFile2 = "/test/song.mp3";
            File downloadFile2 = new File("D:/Downloads/song.mp3");
            OutputStream outputStream2 = new BufferedOutputStream(new FileOutputStream(downloadFile2));
            InputStream inputStream = ftpClient.retrieveFileStream(remoteFile2);
            byte[] bytesArray = new byte[4096];
            int bytesRead = -1;
            while ((bytesRead = inputStream.read(bytesArray)) != -1) {
                outputStream2.write(bytesArray, 0, bytesRead);
            }
 
            success = ftpClient.completePendingCommand();
            if (success) {
                System.out.println("File #2 has been downloaded successfully.");
            }
            outputStream2.close();
            inputStream.close();
 
        } catch (IOException ex) {
            System.out.println("Error: " + ex.getMessage());
            ex.printStackTrace();
        } finally {
            try {
                if (ftpClient.isConnected()) {
                    ftpClient.logout();
                    ftpClient.disconnect();
                }
            } catch (IOException ex) {
                ex.printStackTrace();
            }
        }
    }
}

FTP上传

Apache Commons Net提供上传的方式为

  • boolean storeFile(String remote, InputStream local)
  • OutputStream storeFileStream(String remote)
  • boolean storeUniqueFile(InputStream local)
  • boolean storeUniqueFile(String remote, InputStream local)
  • OutputStream storeUniqueFileStream()
  • OutputStream storeUniqueFileStream(String remote)

代码示例:

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
 
import org.apache.commons.net.ftp.FTP;
import org.apache.commons.net.ftp.FTPClient;
 
/**
 * A program that demonstrates how to upload files from local computer
 * to a remote FTP server using Apache Commons Net API.
 * @author www.codejava.net
 */
public class FTPUploadFileDemo {
 
    public static void main(String[] args) {
        String server = "www.myserver.com";
        int port = 21;
        String user = "user";
        String pass = "pass";
 
        FTPClient ftpClient = new FTPClient();
        try {
 
            ftpClient.connect(server, port);
            ftpClient.login(user, pass);
            ftpClient.enterLocalPassiveMode();
 
            ftpClient.setFileType(FTP.BINARY_FILE_TYPE);
 
            // APPROACH #1: uploads first file using an InputStream
            File firstLocalFile = new File("D:/Test/Projects.zip");
 
            String firstRemoteFile = "Projects.zip";
            InputStream inputStream = new FileInputStream(firstLocalFile);
 
            System.out.println("Start uploading first file");
            boolean done = ftpClient.storeFile(firstRemoteFile, inputStream);
            inputStream.close();
            if (done) {
                System.out.println("The first file is uploaded successfully.");
            }
 
            // APPROACH #2: uploads second file using an OutputStream
            File secondLocalFile = new File("E:/Test/Report.doc");
            String secondRemoteFile = "test/Report.doc";
            inputStream = new FileInputStream(secondLocalFile);
 
            System.out.println("Start uploading second file");
            OutputStream outputStream = ftpClient.storeFileStream(secondRemoteFile);
            byte[] bytesIn = new byte[4096];
            int read = 0;
 
            while ((read = inputStream.read(bytesIn)) != -1) {
                outputStream.write(bytesIn, 0, read);
            }
            inputStream.close();
            outputStream.close();
 
            boolean completed = ftpClient.completePendingCommand();
            if (completed) {
                System.out.println("The second file is uploaded successfully.");
            }
 
        } catch (IOException ex) {
            System.out.println("Error: " + ex.getMessage());
            ex.printStackTrace();
        } finally {
            try {
                if (ftpClient.isConnected()) {
                    ftpClient.logout();
                    ftpClient.disconnect();
                }
            } catch (IOException ex) {
                ex.printStackTrace();
            }
        }
    }
 }

文件夹上传

FTP文件夹上传,简单点说,就如同拷贝一个文件夹一样,首先利用递归遍历文件夹目录, 然后再服务器上创建相应目录的文件夹,然后将文件一个一个上传.具体步骤

  • 遍历文件夹
  • 如果是文件,直接上传,如果是文件夹,则在服务器上创建对应的文件,然后重复1,2,3上传子文件夹
  • 直到返回为空,或者最后一项正在被处理.

代码示例

/**
 * Upload a whole directory (including its nested sub directories and files)
 * to a FTP server.
 *
 * @param ftpClient
 *            an instance of org.apache.commons.net.ftp.FTPClient class.
 * @param remoteDirPath
 *            Path of the destination directory on the server.
 * @param localParentDir
 *            Path of the local directory being uploaded.
 * @param remoteParentDir
 *            Path of the parent directory of the current directory on the
 *            server (used by recursive calls).
 * @throws IOException
 *             if any network or IO error occurred.
 */
public static void uploadDirectory(FTPClient ftpClient,
        String remoteDirPath, String localParentDir, String remoteParentDir)
        throws IOException {
 
    System.out.println("LISTING directory: " + localParentDir);
 
    File localDir = new File(localParentDir);
    File[] subFiles = localDir.listFiles();
    if (subFiles != null && subFiles.length > 0) {
        for (File item : subFiles) {
            String remoteFilePath = remoteDirPath + "/" + remoteParentDir
                    + "/" + item.getName();
            if (remoteParentDir.equals("")) {
                remoteFilePath = remoteDirPath + "/" + item.getName();
            }
 
 
            if (item.isFile()) {
                // upload the file
                String localFilePath = item.getAbsolutePath();
                System.out.println("About to upload the file: " + localFilePath);
                boolean uploaded = uploadSingleFile(ftpClient,
                        localFilePath, remoteFilePath);
                if (uploaded) {
                    System.out.println("UPLOADED a file to: "
                            + remoteFilePath);
                } else {
                    System.out.println("COULD NOT upload the file: "
                            + localFilePath);
                }
            } else {
                // create directory on the server
                boolean created = ftpClient.makeDirectory(remoteFilePath);
                if (created) {
                    System.out.println("CREATED the directory: "
                            + remoteFilePath);
                } else {
                    System.out.println("COULD NOT create the directory: "
                            + remoteFilePath);
                }
 
                // upload the sub directory
                String parent = remoteParentDir + "/" + item.getName();
                if (remoteParentDir.equals("")) {
                    parent = item.getName();
                }
 
                localParentDir = item.getAbsolutePath();
                uploadDirectory(ftpClient, remoteDirPath, localParentDir,
                        parent);
            }
        }
    }
}

/**
 * Upload a single file to the FTP server.
 *
 * @param ftpClient
 *            an instance of org.apache.commons.net.ftp.FTPClient class.
 * @param localFilePath
 *            Path of the file on local computer
 * @param remoteFilePath
 *            Path of the file on remote the server
 * @return true if the file was uploaded successfully, false otherwise
 * @throws IOException
 *             if any network or IO error occurred.
 */
public static boolean uploadSingleFile(FTPClient ftpClient,
        String localFilePath, String remoteFilePath) throws IOException {
    File localFile = new File(localFilePath);
 
    InputStream inputStream = new FileInputStream(localFile);
    try {
        ftpClient.setFileType(FTP.BINARY_FILE_TYPE);
        return ftpClient.storeFile(remoteFilePath, inputStream);
    } finally {
        inputStream.close();
    }
}

import java.io.IOException;
 
import org.apache.commons.net.ftp.FTPClient;
 
/**
 * This test program illustrates how to utilize the FTPUtil class in order
 * to upload a whole directory to a FTP server.
 * @author www.codejava.net
 *
 */
public class FTPUploadDirectoryTest {
 
    public static void main(String[] args) {
        String server = "www.codejava.net";
        int port = 21;
        String user = "username";
        String pass = "password";
 
        FTPClient ftpClient = new FTPClient();
 
        try {
            // connect and login to the server
            ftpClient.connect(server, port);
            ftpClient.login(user, pass);
 
            // use local passive mode to pass firewall
            ftpClient.enterLocalPassiveMode();
 
            System.out.println("Connected");
 
            String remoteDirPath = "/Upload";
            String localDirPath = "E:/Test/Download/FTP/Test";
 
            FTPUtil.uploadDirectory(ftpClient, remoteDirPath, localDirPath, "");
 
            // log out and disconnect from the server
            ftpClient.logout();
            ftpClient.disconnect();
 
            System.out.println("Disconnected");
        } catch (IOException ex) {
            ex.printStackTrace();
        }
    }
}

FTP下载文件夹

  • 递归服务器上的文件夹
  • 下载文件
/**
 * Download a single file from the FTP server
 * @param ftpClient an instance of org.apache.commons.net.ftp.FTPClient class.
 * @param remoteFilePath path of the file on the server
 * @param savePath path of directory where the file will be stored
 * @return true if the file was downloaded successfully, false otherwise
 * @throws IOException if any network or IO error occurred.
 */
public static boolean downloadSingleFile(FTPClient ftpClient,
        String remoteFilePath, String savePath) throws IOException {
    File downloadFile = new File(savePath);
     
    File parentDir = downloadFile.getParentFile();
    if (!parentDir.exists()) {
        parentDir.mkdir();
    }
         
    OutputStream outputStream = new BufferedOutputStream(
            new FileOutputStream(downloadFile));
    try {
        ftpClient.setFileType(FTP.BINARY_FILE_TYPE);
        return ftpClient.retrieveFile(remoteFilePath, outputStream);
    } catch (IOException ex) {
        throw ex;
    } finally {
        if (outputStream != null) {
            outputStream.close();
        }
    }
}

/**
 * Download a whole directory from a FTP server.
 * @param ftpClient an instance of org.apache.commons.net.ftp.FTPClient class.
 * @param parentDir Path of the parent directory of the current directory being
 * downloaded.
 * @param currentDir Path of the current directory being downloaded.
 * @param saveDir path of directory where the whole remote directory will be
 * downloaded and saved.
 * @throws IOException if any network or IO error occurred.
 */
public static void downloadDirectory(FTPClient ftpClient, String parentDir,
        String currentDir, String saveDir) throws IOException {
    String dirToList = parentDir;
    if (!currentDir.equals("")) {
        dirToList += "/" + currentDir;
    }
 
    FTPFile[] subFiles = ftpClient.listFiles(dirToList);
 
    if (subFiles != null && subFiles.length > 0) {
        for (FTPFile aFile : subFiles) {
            String currentFileName = aFile.getName();
            if (currentFileName.equals(".") || currentFileName.equals("..")) {
                // skip parent directory and the directory itself
                continue;
            }
            String filePath = parentDir + "/" + currentDir + "/"
                    + currentFileName;
            if (currentDir.equals("")) {
                filePath = parentDir + "/" + currentFileName;
            }
 
            String newDirPath = saveDir + parentDir + File.separator
                    + currentDir + File.separator + currentFileName;
            if (currentDir.equals("")) {
                newDirPath = saveDir + parentDir + File.separator
                          + currentFileName;
            }
 
            if (aFile.isDirectory()) {
                // create the directory in saveDir
                File newDir = new File(newDirPath);
                boolean created = newDir.mkdirs();
                if (created) {
                    System.out.println("CREATED the directory: " + newDirPath);
                } else {
                    System.out.println("COULD NOT create the directory: " + newDirPath);
                }
 
                // download the sub directory
                downloadDirectory(ftpClient, dirToList, currentFileName,
                        saveDir);
            } else {
                // download the file
                boolean success = downloadSingleFile(ftpClient, filePath,
                        newDirPath);
                if (success) {
                    System.out.println("DOWNLOADED the file: " + filePath);
                } else {
                    System.out.println("COULD NOT download the file: "
                            + filePath);
                }
            }
        }
    }
}

import java.io.IOException;
 
import org.apache.commons.net.ftp.FTPClient;
 
public class FTPDownloadDirectoryTest {
 
    public static void main(String[] args) {
        String server = "www.codejava.net";
        int port = 21;
        String user = "username";
        String pass = "password";
 
        FTPClient ftpClient = new FTPClient();
 
        try {
            // connect and login to the server
            ftpClient.connect(server, port);
            ftpClient.login(user, pass);
 
            // use local passive mode to pass firewall
            ftpClient.enterLocalPassiveMode();
 
            System.out.println("Connected");
 
            String remoteDirPath = "/Test";
            String saveDirPath = "E:/Test/Download/FTP";
 
            FTPUtil.downloadDirectory(ftpClient, remoteDirPath, "", saveDirPath);
 
            // log out and disconnect from the server
            ftpClient.logout();
            ftpClient.disconnect();
 
            System.out.println("Disconnected");
        } catch (IOException ex) {
            ex.printStackTrace();
        }
    }
}

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,835评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,598评论 1 295
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,569评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,159评论 0 213
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,533评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,710评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,923评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,674评论 0 203
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,421评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,622评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,115评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,428评论 2 254
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,114评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,097评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,875评论 0 197
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,753评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,649评论 2 271

推荐阅读更多精彩内容

  • ftp 文件传输协议 跨平台 上传下载文件 vsftpd 工具:非常安全的文件传输协议;默认的命令端口21号,数据...
    柒夏锦阅读 3,863评论 1 9
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,100评论 18 139
  • FTP服务概述 简单FTP构建及访问 VSFTP服务基础 用户禁锢、黑白名单 FTP并发及带宽限制 一、FTP服务...
    紫_轩阅读 7,455评论 3 25
  • 今天读的是稲和盛夫的《活法》 不管是在工作上还是在学习上,我们都要理论联系实践。空有理论,没有亲自去积累经验很快就...
    我是牛阳阳阅读 201评论 1 4
  • 阅读一遍规画:已读; 一句爱的表达:对先生和女儿说我爱你。 一次自我确认:我是智慧的源泉 一个微笑:n个微笑 三个...
    李艳萍的花园阅读 132评论 0 0