当前位置: 代码迷 >> 综合 >> ubuntu16.04 OS搭建FTP服务器
  详细解决方案

ubuntu16.04 OS搭建FTP服务器

热度:64   发布时间:2023-10-15 06:57:18.0

FTP: File Transfer Protocol文件传输协议,两台计算机传送文件的协议,客户端可以通过FTP命令从服务器下载,上传文件,修改目录。可以通过命令vsftpd -version查看是否安装了vsftpd。

FTP支持以下两种工作模式:

  • 主动模式:客户端向FTP服务器发送端口信息,由服务器主动连接该端口。
  • 被动模式:FTP服务器开启并发送端口信息给客户端,由客户端连接该端口,服务器被动接受连接。

说明 大多数FTP客户端都在局域网中,没有独立的公网IP地址,且有防火墙阻拦,主动模式下FTP服务器成功连接到客户端比较困难。因此,如无特殊需求,建议您将FTP服务器配置为被动模式。

FTP支持以下三种认证模式:

  • 匿名用户模式:任何人无需密码验证就可以直接登录到FTP服务器。这种模式最不安全,一般只用来保存不重要的公开文件,不推荐在生产环境中使用。
  • 本地用户模式:通过Linux系统本地账号进行验证的模式,相较于匿名用户模式更安全。
  • 虚拟用户模式:FTP服务器的专有用户。虚拟用户只能访问Linux系统为其提供的FTP服务,而不能访问Linux系统的其它资源,进一步增强了FTP服务器的安全性。

一、ftp服务端的安装

# 获得最近的软件包的列表
sudo apt-get update 
# 安装FTP服务端
sudo apt-get install vsftpd
# 检测是否安装成功
vsftpd -version     

看到如下信息,则表明vsftpd安装成功:


root@wangmin-virtual-machine:~# vsftpd -version
vsftpd: version 3.0.3

二、ftp服务端的配置

FTP服务的默认配置文件是:/etc/vsftpd.conf,在配置之前我们最好先把原文件备份一下,命令如下:

cp /etc/vsftpd.conf /etc/vsftpd_bak.conf

执行vim /etc/vsftd.conf命令,编辑配置文件如下:

# 是否开启监听ipv4和ipv6数据  
listen=YES
# 是否开启监听ipv6数据
listen_ipv6=NO
# Allow anonymous FTP? (Disabled by default).
# 是否允许匿名用户登陆,无需密码
anonymous_enable=NO
# Uncomment this to allow local users to log in.
# 是否允许本地用户登录
local_enable=YES
# Uncomment this to enable any form of FTP write command.
# 是否允许登陆者上传文件
write_enable=YES
# 设置本地用户默认要减免的权限
#local_umask=022
# 是否允许匿名用户上传文件
anon_upload_enable=NO
# 是否允许匿名用户新建文件夹
anon_mkdir_write_enable=NO
# 目录消息,能够给远程登陆的用户发送目录
dirmessage_enable=YES
# 服务器所展示的目录将随着本地时间而改变
use_localtime=YES
# Activate logging of uploads/downloads.
# 开启上传下载的日志记录
xferlog_enable=YES
# Make sure PORT transfer connections originate from port 20 (ftp-data).
# 确认连接传输的端口号为20
connect_from_port_20=YES
#默认注释了,开启匿名用户上传用户映射,和下面一行一起使用 48行
#chown_uploads=YES
#默认注释了,将匿名用户上传的文件的用户映射为whoever用户 49行
#chown_username=whoever
# 日志文件存放位置
xferlog_file=/var/log/vsftpd.log
# 日志文件采用标准格式
xferlog_std_format=YES
# You may fully customise the login banner string:
# 在使用shell时登陆那么会发送欢迎语
ftpd_banner=Welcome to blah FTP service.
# 对本地用户是否实施限制
#chroot_local_user=YES
# 对本地用户是否实施限制
chroot_local_user=YES
# 开启限制白名单
chroot_list_enable=YES
# (default follows)允许chroot_list文件中配置的用户登录此ftp服务器
# 白名单路径,若无这个文件需要自己创建
chroot_list_file=/etc/vsftpd.chroot_list
# 设置PAM认证服务的配置文件名称
# pam_service_name=vsftpd
# Uncomment this to indicate that vsftpd use a utf8 filesystem.
# 编码统一为utf8编码,可以识别中文,防止乱码
utf8_filesystem=YES# 配置ftp服务器的上传下载文件所在的根目录
local_root=/home/ftp
#开启被动模式,若不指定,默认就是被动模式
#pasv_enable=YES
allow_writeable_chroot=YES
#本教程中为Linux实例公网IP
pasv_address=1xx.1XX.1XX.1XX
#设置被动模式下,建立数据传输可使用的端口范围的最小值
#pasv_min_port=50000
#设置被动模式下,建立数据传输可使用的端口范围的最大值
#pasv_max_port=51000
#说明 建议您把端口范围设置在一段比较高的范围内,例如50000~50010,有助于提高访问FTP服务器的安全性

注意 修改和添加配置文件内的信息时,请注意格式问题。例如,添加多余的空格会造成无法重启服务的结果。

用户登录控制参数说明如下表所示。

参数 说明
anonymous_enable=YES 接受匿名用户
no_anon_password=YES 匿名用户login时不询问口令
anon_root=(none) 匿名用户主目录
local_enable=YES 接受本地用户
local_root=(none) 本地用户主目录

用户权限控制参数说明如下表所示。

参数 说明
write_enable=YES 可以上传文件(全局控制)
local_umask=022 本地用户上传的文件权限
file_open_mode=0666 上传文件的权限配合umask使用
anon_upload_enable=NO 匿名用户可以上传文件
anon_mkdir_write_enable=NO 匿名用户可以建目录
anon_other_write_enable=NO 匿名用户修改删除
chown_username=lightwiter 匿名上传文件所属用户名

三、创建FTP用户及用户组

# 创建一个供FTP服务使用的文件目录
mkdir /home/ftp
# 赋予FTP用户及用组读写权限
chmod 775 -R /home/ftp
# 为FTP服务创建一个Linux用户。本示例中,该用户名为ftptest
sudo adduser ftptest
# 修改ftptest用户的密码
sudo passwd ftptest
# 创建ftpusers用户组
sudo groupadd ftpusers
# 将这个新用户加入到ftpusers用户组中
usermod -G ftpusers ftptest
# 更改/home/ftp目录的拥有者为ftptest
chown -R ftptest:ftpusers /home/ftp

 将该用户加入vsftpd.chroot_list白名单中: 

mkdir /etc/vsftpd.chroot_list
vim /etc/vsftpd.chroot_list

添加如下内容:

# FTP用户白名单
ftptest

四、FTP服务器的启停命令

service vsftpd start
service vsftpd stop 
service vsftpd restart
# 查看FTP服务器监听的端口
netstat -antup | grep ftp# 运行以下命令设置FTP服务开机自启动。
systemctl enable vsftpd.service
# 运行以下命令启动FTP服务。
systemctl start vsftpd.service
# 查看FTP服务器的运行状态
systemctl status vsftpd.service
# 看到active (running),表明FTP服务器运行正常
root@wangmin-virtual-machine:/etc# systemctl status vsftpd.service
● vsftpd.service - vsftpd FTP serverLoaded: loaded (/lib/systemd/system/vsftpd.service; enabled; vendor preset: enabled)Active: active (running) since 六 2020-10-31 08:15:29 CST; 45min agoMain PID: 920 (vsftpd)CGroup: /system.slice/vsftpd.service└─920 /usr/sbin/vsftpd /etc/vsftpd.conf10月 31 08:15:29 wangmin-virtual-machine systemd[1]: Starting vsftpd FTP server...
10月 31 08:15:29 wangmin-virtual-machine systemd[1]: Started vsftpd FTP server.
10月 31 09:00:55 wangmin-virtual-machine systemd[1]: Started vsftpd FTP server.

五、客户端测试

FTP客户端、Windows命令行工具或浏览器均可用来测试FTP服务器。本教程以Google Chrome浏览器为例,介绍FTP服务器的访问步骤。

说明 使用浏览器访问FTP服务器出错时,建议您清除浏览器缓存后再尝试。

  1. 打开客户端的Google Chrome浏览器。
  2. 在地址栏中输入ftp://<FTP服务器公网IP地址>:21

    本教程中为Linux实例的公网IP地址。

  3. 在弹出的对话框中,输入用户名ftptest和对应的密码,即可对FTP文件进行相应权限的操作。

全部测试方法如下:

方法一:打开浏览器,在地址栏输入:ftp://ip

方法二: 在ubuntu中使用shell输入:ftp ip_address

方法三: 在windows中在文件管理器地址栏输入:ftp://ip_addresss,该方式可以上传下载文件

方法四: 在windows中使用cmd输入:ftp://ip_addresss //显示连接成功

六、springboot实现FTP文件上传与下载

1、pom.xml引入依赖

		<dependency><groupId>commons-net</groupId><artifactId>commons-net</artifactId><version>3.6</version></dependency>

2、application.properties添加配置

# ftp相关配置
ftp.host=192.168.188.*
# ftp端口号
ftp.port=21
# ftp请求的用户名
ftp.username=ftptest
# ftp请求的密码
ftp.password=1XXX
# ftp请求读取写入的文件路径
ftp.filepath=/home/ftp

3、FtpConfig

import java.io.IOException;
import java.net.SocketException;
import org.apache.commons.net.ftp.FTP;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPReply;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;@Configuration
@Slf4j
@Getter
@Setter
public class FtpConfig {/*** ftp站点*/@Value("${ftp.host}")private String ftpHost;/*** ftp端口号*/@Value("${ftp.port}")private int ftpPort;/*** ftp访问用户名*/@Value("${ftp.username}")private String ftpUsername;/*** ftp访问密码*/@Value("${ftp.password}")private String ftpPassword;/*** ftp访问文件路径*/@Value("${ftp.filepath}")private String ftpFilepath;@Beanpublic FTPClient ftpClient(FtpConfig ftpConfig) {FTPClient ftpClient = new FTPClient();ftpClient.setConnectTimeout(1000 * 30); // 设置连接超时时间,单位:毫秒ftpClient.setControlEncoding("UTF-8"); // 设置ftp字符集ftpClient.enterLocalPassiveMode(); // 设置被动模式,文件传输端口设置try {ftpClient.connect(ftpConfig.getFtpHost(),ftpConfig.getFtpPort());ftpClient.setFileType(FTP.BINARY_FILE_TYPE);// 设置文件传输模式为二进制,可以保证传输的内容不会被改变ftpClient.login(ftpConfig.getFtpUsername(), ftpConfig.getFtpPassword());int replyCode = ftpClient.getReplyCode();// 所有以2开头的代码都是连接成功的响应if (!FTPReply.isPositiveCompletion(replyCode)) {ftpClient.disconnect();log.error("connect to ftp server fail!,replyCode is {}",replyCode);return null;} else {log.info("connect to ftp server success,replyCode is {}",replyCode);return ftpClient;}} catch (SocketException e) {log.error("connect to ftp server fail,{}", e);return null;} catch (IOException e) {log.error("connect to ftp server fail,{}", e);return null;}}
}

4、FtpService提供上传、下载方法和删除文件的方法

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Objects;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPFile;
import org.apache.tomcat.util.http.fileupload.IOUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import lombok.extern.slf4j.Slf4j;@Service
@Slf4j
public class FtpService {@Autowiredprivate FTPClient ftpClient;/*** 下载文件* @param remoteFileName 远程文件名称* @param remoteDir 远程文件目录* @param descDir 下载文件保存路径* @return boolean*/public boolean downloadFile(String remoteFileName, String remoteDir, String descDir) {if (Objects.isNull(ftpClient)) {log.error("ftpClient is null!");return false;}if (StringUtils.isEmpty(remoteFileName) || StringUtils.isEmpty(remoteDir)) {log.error("{} or {} is null!", remoteFileName, remoteDir);return false;}InputStream inputStream = null;OutputStream outputStream = null;try {ftpClient.changeWorkingDirectory(remoteDir);FTPFile[] ftpFiles = ftpClient.listFiles(remoteDir);if (ftpFiles.length == 0) {log.error("{},no files found in this directory", remoteDir);return false;}// 遍历当前目录下的文件,判断要读取的文件是否在当前目录下for (FTPFile ftpFile : ftpFiles) {if (ftpFile.isFile()) {if (ftpFile.getName().equals(remoteFileName)) {// 获取待读文件输入流inputStream = ftpClient.retrieveFileStream(remoteFileName);outputStream = new FileOutputStream(new File(descDir + remoteFileName));// 文件大小 < 2G,请使用IOUtils.copy(input, output)// 文件大小 > 2G,请使用IOUtils.copyLarge(input, output)long available = IOUtils.copyLarge(inputStream, outputStream);if (available < 0) {log.error("copy failed!");return false;}outputStream.flush();}}else {log.info("{} is a directory",ftpFile);}}log.info("download file {} success", remoteFileName);ftpClient.logout();} catch (IOException e) {log.error("download file fail,{}", e);return false;} finally {if (ftpClient.isConnected()) {try {ftpClient.disconnect();} catch (IOException e) {log.error("disconnect fail,{}", e);return false;}}if (inputStream != null) {try {inputStream.close();} catch (IOException e) {log.error("inputStream close fail,{}", e);return false;}}if (outputStream != null) {try {outputStream.close();} catch (IOException e) {log.error("outputStream close fail,{}", e);return false;}}}return true;}/*** 上传文件* * @param inputStream 待上传文件的输入流* @param originName 文件保存时的名字* @param remoteDir 文件要存放的目录*/public boolean uploadFile(InputStream inputStream, String originName, String remoteDir) {if (Objects.isNull(ftpClient)) {log.error("ftpClient is null!");return false;}try {// 进入到文件保存的目录
boolean changeDir = ftpClient.changeWorkingDirectory(remoteDir);if(changeDir) {
// 保存文件boolean storeFile = ftpClient.storeFile(originName, inputStream);ftpClient.logout();
if(storeFile) {              log.info("{} upload success", originName);return true;}else {log.error("store file fail,{}",originName);return false;}}else {log.error("change working directory fail,{}",remoteDir);return false;}       } catch (IOException e) {log.error("{} upload fail,{}", originName, e);return false;} finally {if (ftpClient.isConnected()) {try {ftpClient.disconnect();} catch (IOException e) {log.error("disconnect fail ------->>>{}", e);}}}
}/*** 删除指定路径下的指定文件;若文件名为空,则删除指定路径下的全部文件* @param fileName* @param pathname* @return boolean*/public boolean deleteFile(String fileName, String pathname) {if (Objects.isNull(ftpClient)) {log.error("ftpClient is null!");return false;}if (StringUtils.isEmpty(pathname)) {log.error("pathname is null!");return false;}try {boolean changeDir = ftpClient.changeWorkingDirectory(pathname);if (changeDir) {FTPFile[] ftpFiles = ftpClient.listFiles();for (FTPFile ftpFile : ftpFiles) {if (ftpFile.isDirectory()) {if (ftpClient.removeDirectory(ftpFile.getName())) {continue;} else {log.error("delete {} fail", ftpFile.getName());return false;}} else if (StringUtils.isEmpty(fileName)) {// 删除全部文件if (ftpClient.deleteFile(ftpFile.getName())) {continue;} else {log.error("delete {} fail", ftpFile.getName());return false;}} else if (ftpFile.getName().equals(fileName)) {return ftpClient.deleteFile(fileName);}}return true;} else {log.error("change working directory fail,{}", pathname);return false;}} catch (IOException e) {log.error("delete file fail,{}", e);return false;}}
}

5、测试类

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import com.summer.App;
import com.summer.ftp.FtpService;
import lombok.extern.slf4j.Slf4j;@Slf4j
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = App.class)
public class FtpTest {@AutowiredFtpService ftpService;@Testpublic void uploadFile() {String filePath = "C:\\Users\\Administrator\\Desktop\\vsftpd.conf";InputStream inputStream = null;try {inputStream = new FileInputStream(new File(filePath));String originName = "vsftpd.conf";String remoteDir = "/home/ftpuser/";ftpService.uploadFile(inputStream, originName, remoteDir);log.info(">>>>>>>>>文件上传成功!");} catch (FileNotFoundException e) {e.printStackTrace();}}@Testpublic void download() {ftpService.downloadFile("backlog.txt", "/home/ftpuser/", "C://");
}@Testpublic void deleteFile() {String fileName = null;//"vsftpd.conf";String pathname = "/home/ftpuser/";boolean deleteFile = ftpService.deleteFile(fileName, pathname );if(deleteFile) {log.info(">>>>>>>>删除文件成功!");}else {log.error(">>>>>>>>删除文件失败!");}}
}

 

  相关解决方案