FTP协议实现上传和下载
2018-06-21 本文已影响161人
Showdy
FTP协议上传下载
tags: java
简介
FTP
是File 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
andCopyStreamException
).
此外, 当使用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();
}
}
}