1.yum安装FTP服务
yum install vsftpd
2.修改配置
vi /etc/vsftpd/vsftpd.conf
设置监听端口 listen_port=7019
vim /etc/pam.d/vsftpd
注释这一行#auth required pam_shells.so
3.启动服务
cetoos 7 操作为 /bin/systemctl restart vsftpd.service
centoos 6 操作为 service vsftpd restart
4.设置开机启动
chkconfig --level 35 vsftpd on
5.添加用户
useradd ftpadmin -s /sbin/nologin
6.设置密码
passwd ftpadmin (之后输入两次密码)
安装方式2
搭建FTP服务器:
关闭linux防火墙
查看状态:systemctl status firewalld.service
永久关闭:systemctl disable firewalld.service
暂停:systemctl stop firewalld.service
关闭selinux
查看状态:getenforce(最终显示为disabled)
配置:vim /etc/selinux/config
修改为:SELINUX=disabled
暂停:setenforce 0
重启服务器:reboot
创建用户组:
groupadd ftpGroup
创建用户:
useradd -d /data/ftp -s /sbin/nologin -g ftpGroup -G root ftpUser
设置用户密码:
passwd ftpUser
用户FTP文件夹赋权限:
chown ftpUser /data/ftp
cd /data
chmod 777 ftp
安装FTP:
yum install -y vsftpd
设置开机启动:
chkconfig vsftpd on
查看FTP接口:
netstat -antup|grep ftp
进入vsftpd主配置文件:
vim /etc/vsftpd/vsftpd.conf
修改为anonymous_enable = NO,这样可以禁止匿名登陆用户登录
重启FTP服务:
service vsftpd restart
530 500 等错误处理:
vim /etc/pam.d/vsftpd
注释掉:auth required pam_shells.so
(或将auth required pam_shells.so修改为->auth required pam_nologin.so即可)
系统内部尝试自己访问:
ftp ip
外网访问-浏览器访问:
ftp://ip
FTP工具访问:
java 代码上传连接
pom文件
<dependency>
<groupId>commons-net</groupId>
<artifactId>commons-net</artifactId>
<version>3.6</version>
</dependency>
import org.apache.commons.net.ftp.FTP;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPFile;
import org.apache.commons.net.ftp.FTPReply;
import java.io.*;
public class MyFtpUtil {
final private static int sum = 100;//最大进度条
final private static String logo = "=";//进度标识
/**
* 获取FTP服务器客户端
*
* @param hostname 主机名
* @param port 端口
* @param username 用户名
* @param password 密码
* @param type 请求模式,主动与被动 z/b
* @return ftp客户端
* @throws Exception
*/
public static FTPClient getClient(String hostname, int port, String username, String password, String type) throws Exception {
FTPClient ftpClient = new FTPClient();
ftpClient.connect(hostname, port);
ftpClient.setControlEncoding("GBK");
//连接与登录异常排查
if (!FTPReply.isPositiveCompletion(ftpClient.getReplyCode()) || !ftpClient.login(username, password)) {
throw new Exception();
}
if (type.equals("z")) {//设置主动传输或者被动传输
ftpClient.enterLocalActiveMode();
} else if (type.equals("b")) {
ftpClient.enterLocalPassiveMode();
}
ftpClient.setFileType(FTP.BINARY_FILE_TYPE);//设置以二进制方式传输
return ftpClient;
}
/**
* 释放资源
*
* @throws IOException
*/
public static void close(Object... objects) throws IOException {
for (Object object : objects) {
if (object instanceof FTPClient) {
FTPClient ftpClient = (FTPClient) object;
if (ftpClient != null && ftpClient.isConnected()) {
ftpClient.disconnect();
}
} else if (object instanceof InputStream) {
InputStream inputStream = (InputStream) object;
if (inputStream != null) {
inputStream.close();
}
} else if (object instanceof OutputStream) {
OutputStream outputStream = (OutputStream) object;
if (outputStream != null) {
outputStream.close();
}
} else if (object instanceof RandomAccessFile) {
RandomAccessFile randomAccessFile = (RandomAccessFile) object;
if (randomAccessFile != null) {
randomAccessFile.close();
}
}
}
}
/**
* 显示最长进度与历史进度
*
* @param process 历史进度条
*/
private static void historySchedule(Long process) {
System.out.print("总量: ");
for (int i = 0; i < sum; i++) {
System.out.print(logo);
}
System.out.println();
System.out.print("当前: ");
for (int i = 0; i < process; i++) {
System.out.print(logo);
}
}
/**
* 进度增加
*
* @param status 是否结束进度换行
*/
private static void addschedule(boolean status, long frequency) {
if (status) {
System.out.println();
} else {
for (int i = 0; i < frequency; i++) {
System.out.print(logo);
}
}
}
/**
* @param byteLength 传输大小,b
* @param startTime 起始时间,毫秒
* @param endTime 结束时间,毫秒
*/
private static void calculationSpeed(long byteLength, long startTime, long endTime) {
double scends = (endTime - startTime) / 1000.0;//处理时间秒
double v = byteLength / scends;
String unit[] = new String[]{"b", "k", "M", "G", "T", "P"};
int i = 0;
while (v > 1 << 10) {//v < 1024
i++;
v = v / 1024.0;
}
System.out.println(String.format("速度: %.2f %s/s", v, unit[i]));
System.out.println(String.format("传输时间: %.2f 秒", scends));
System.out.println(String.format("传输大小: %s b", byteLength));
}
/**
* 递归创建远程服务器目录
*
* @param serverDirPath 远程服务器文件绝对路径
* @param ftpClient FTPClient对象
* @return 目录创建是否成功
* @throws IOException
*/
public static void createDirecroty(String serverDirPath, FTPClient ftpClient) throws IOException {
String directory = serverDirPath.substring(0, serverDirPath.lastIndexOf("/") + 1);
if (!directory.equalsIgnoreCase("/") && !ftpClient.changeWorkingDirectory(new String(directory.getBytes("GBK"), "iso-8859-1"))) {
//如果远程目录不存在,则递归创建远程服务器目录
int end, start = 0;
if (directory.startsWith("/")) {
start = 1;
}
end = directory.indexOf("/", start);
while (true) {
String subDirectory = new String(serverDirPath.substring(start, end).getBytes("GBK"), "iso-8859-1");
if (!ftpClient.changeWorkingDirectory(subDirectory)) {
if (ftpClient.makeDirectory(subDirectory)) {
ftpClient.changeWorkingDirectory(subDirectory);
} else {
throw new IOException("创建远程目录失败");
}
}
start = end + 1;
end = directory.indexOf("/", start);
//检查所有目录是否创建完毕
if (end <= start) {
break;
}
}
}
}
/**
* 上传文件控制端
*
* @param ftpClient ftp客户端对象
* @param localFilePath 本地文件名称,绝对路径
* @param serverFilePath 远程文件路径,支持递归创建不存在的目录结构
* @param buffer 上传文件buffer缓存大小
* @return 上传结果
* @throws IOException
*/
public static String uploadController(FTPClient ftpClient, String localFilePath, String serverFilePath, int buffer) throws IOException {
//设置以二进制流的方式传输
ftpClient.setFileType(FTP.BINARY_FILE_TYPE);
ftpClient.setControlEncoding("GBK");
//对远程目录的处理
String serverFileName = serverFilePath;
if (serverFilePath.contains("/")) {
serverFileName = serverFilePath.substring(serverFilePath.lastIndexOf("/") + 1);
//创建服务器远程目录结构,创建失败直接抛出异常返回
createDirecroty(serverFilePath, ftpClient);
}
//检查远程是否存在文件
long serverFileSize = 0;
File f = new File(localFilePath);
if (!f.exists()) {
throw new IOException("本地文件不存在");
}
FTPFile[] files = ftpClient.listFiles(new String(serverFileName.getBytes("GBK"), "iso-8859-1"));
if (files.length == 1) {
serverFileSize = files[0].getSize();
long localFileSize = f.length();
if (serverFileSize == localFileSize) {
throw new IOException("文件已经存在");
} else if (serverFileSize > localFileSize) {
throw new IOException("远程文件大于本地文件");
}
}
//serverFileSize表示移动文件内读取指针,实现断点续传
try {
uploadFile(ftpClient, serverFileName, f, serverFileSize, buffer);
} catch (IOException e) {
if (!ftpClient.deleteFile(serverFileName)) {//如果断点续传没有成功,则删除服务器上文件,重新上传
throw new IOException("上传失败,重传删除文件失败");
}
uploadFile(ftpClient, serverFileName, f, serverFileSize, buffer);
}
return "success";
}
/**
* 上传文件到服务器,新上传和断点续传
*
* @param ftpClient FTPClient引用
* @param serverFile 远程文件名,在上传之前已经将服务器工作目录做了改变
* @param localFile 本地文件File句柄,绝对路径
* @param serverFileSize 文件偏移量
* @param buffer 上传文件buffer大小
* @return
* @throws IOException
*/
public static void uploadFile(FTPClient ftpClient, String serverFile, File localFile, long serverFileSize, int buffer) throws IOException {
long startTime = System.currentTimeMillis();//起始时间
Long fileLength = localFile.length();//本地文件大小
long process;//历史进度
if (fileLength != 0) {
process = ((serverFileSize * sum) / fileLength);
} else {
process = sum;
}
long remaining = fileLength - serverFileSize;//计算文件传输大小
historySchedule(process);//显示完成进度与历史进度
RandomAccessFile raf = new RandomAccessFile(localFile, "r");
OutputStream out = ftpClient.appendFileStream(new String(serverFile.getBytes("GBK"), "iso-8859-1"));
//断点续传
if (serverFileSize > 0) {
ftpClient.setRestartOffset(serverFileSize);//设置远程文件偏移量
raf.seek(serverFileSize);//偏移读取本地文件
}
byte[] bytes = new byte[buffer];
int c;
while ((c = raf.read(bytes)) != -1) {
out.write(bytes, 0, c);
serverFileSize += c;
long temp = (serverFileSize * sum) / fileLength;//当前进度
long addschedule = temp - process;//需要增加的进度条数
process = temp;//当前进度内存更新
if (addschedule > 0) {
addschedule(false, addschedule);//打印进度
}
}
addschedule(true, 0);//换行
close(out, raf);//关闭资源
boolean result = ftpClient.completePendingCommand();
if (!result) {
throw new IOException("上传失败");
}
//计算还需要传输的文件大小
long endTime = System.currentTimeMillis();//结束时间
calculationSpeed(remaining, startTime, endTime);
}
/**
* 从FTP服务器上下载文件,支持断点续传,上传百分比汇报
*
* @param ftpClient ftp客户端
* @param serverFilePath 远程文件路径
* @param localFilePath 本地文件路径
* @param buffer 下载文件缓存大小
* @return 上传的状态
* @throws IOException
*/
public static String download(FTPClient ftpClient, String serverFilePath, String localFilePath, int buffer) throws Exception {
long startTime = System.currentTimeMillis();//起始时间
//检查远程文件是否存在
FTPFile[] files = ftpClient.listFiles(new String(serverFilePath.getBytes("GBK"), "iso-8859-1"));
if (files.length == 0) {
throw new IOException("下载的文件不存在");
}
long serverFileSize = files[0].getSize();//远程文件大小
long localFileSize = 0;//本地文件大小
File f = new File(localFilePath);
long localTemp = 0L;
//本地存在文件,进行断点下载
if (f.exists()) {
localFileSize = f.length();
//判断本地文件大小是否大于远程文件大小
if (localFileSize > serverFileSize) {//本地文件与服务器文件大小不同
throw new IOException("本地文件已经大于服务器文件");
} else if (localFileSize == serverFileSize) {//本地文件已经存在
throw new IOException("本地文件已经存在");
}
}
long remaining = serverFileSize - localTemp;//计算文件传输大小
long process;
if (serverFileSize != 0) {
process = ((localFileSize * sum) / serverFileSize);//历史进度
} else {
process = sum;
}
historySchedule(process);//显示完成进度与历史进度
FileOutputStream out = new FileOutputStream(f, true);//追加内容
ftpClient.setRestartOffset(localFileSize);
InputStream in = ftpClient.retrieveFileStream(new String(serverFilePath.getBytes("GBK"), "iso-8859-1"));
byte[] bytes = new byte[buffer];
int c;
while ((c = in.read(bytes)) != -1) {
out.write(bytes, 0, c);
localFileSize += c;
long temp = (localFileSize * sum) / serverFileSize;//当前进度
long addschedule = temp - process;//需要增加的进度条数
process = temp;//当前进度内存更新
if (addschedule > 0) {
addschedule(false, addschedule);//打印进度
}
}
addschedule(true, 0);//换行
close(in, out);
boolean isDo = ftpClient.completePendingCommand();
if (!isDo) {
throw new IOException("下载文件失败");
}
long endTime = System.currentTimeMillis();//结束时间
calculationSpeed(remaining, startTime, endTime);
return "ok";
}
public static void main(String[] args) throws Exception {
String localPath = "D:\\2.txt";//226,668,403
String serverPath = "./3.txt";
FTPClient client = getClient("39.100.228.221", 21, "admin", "123456", "z");
download(client, serverPath, localPath, 20000);
}
}
FTP操作分为两种 (主动模式与被动模式)
主动模式:
客户端访问服务端,二者交互是通过客户端的端口交互,客户端不能有防火墙
被动模式:
客户端与服务端交互,二者交互是通过服务端的固定端口交互,服务端不能有防火墙
被动模式如果服务端有防火墙,那么服务端需要开放一些端口用于与客户端交互
#被动模式是否开启
pasv_enable=YES
#被动模式交互,服务端使用端口范围
pasv_min_port=9910
pasv_max_port=9900
主动模式需要关闭客户端的防火墙