目录
FTP简介FTP架构FTP数据连接模式用户认证客户端Vsftp安装与配置启动服务配置文件说明传输模式配置系统用户配置java操作ftp文件服务器
1.引入依赖2.提供接口3.对提供操作ftp接口进行实现4.配置ftp相关参数5.写测试controller
FTP简介
文件传输协议(File Transfer Protocol,FTP)是用于在网络上进行文件传输的一套标准协议,它工作在 OSI 模型的第七层,TCP 模型的第四层, 即应用层, 使用 TCP 传输而不是 UDP, 客户在和服务器建立连接前要经过一个“三次握手”的过程, 保证客户与服务器之间的连接是可靠的, 而且是面向连接, 为数据传输提供可靠保证。
FTP也是一个应用程序,基于不同的操作系统有不同的FTP应用程序,而所有这些应用程序都遵守同一种协议以传输文件。可以使用FTP进行下载和上传。
文件传送协议FTP(File Transfer Protocol)是Internet上使用比较广泛的文件传送协议。FTP提供交互式的访问,允许客户指明文件的类型与格式,并允许文件具有存取权限。FTP屏蔽了各种计算机系统的细节,因此适用于在异构网络中任意计算机间传送文件。它的基本应用就是将文件从一台计算机复制到另一台计算机中。它要存取一个文件,就必须先获得一个本地文件的副本,如果修改文件,也只能对文件的副本进行修改,然后再将修改后的文件副本传回到原节点。
您只要记住几个关键词:交互式、存取权限和副本。
简单文件传送协议TFTP(Trivial File Transfer Protocol)是一个小而易于实现的文件传送协议。TFTP是基于UDP数据报,需要有自己的差错改正措施。TFTP只支持文件传输,不支持交互,没有庞大的命令集。也没有目录列表功能,以及不能对用户进行身份鉴别。但它的代码所占内存较小,不需要硬盘就可以固化TFTP代码,很适合较小的计算机和特殊用途的设备。
您会发现TFTP和FTP一个主要的区别就是它没有交互式,且不进行身份验证。
FTP的客户可以是任意平台
FTP标准端口:
FTP架构
FTP监听于TCP的21号端口,是一种C/S架构的应用程序。在Linux中常用的服务端软件和客户端软件一般如下表所示:
FTP数据连接模式
FTP有2种数据连接模式:命令连接和数据连接
命令连接:是指文件管理类命令,始终在线的持久性连接,直到用户退出登录为止(可以简单的理解为建立连接)数据连接:是指数据传输,按需创建及关闭的连接(可以简单的理解为传输数据)
数据连接需要关注的点有:
1.数据传输格式
2.数据传输模式
主动模式:由服务器端创建数据连接被动模式:由客户端创建数据连接
两种数据传输模式的建立过程:
1.主动模式
命令连接(建立连接通道)
Client端以一个1024以上的随机端口(端口号大于1023小于65535)来连接Server端的21号端口
数据连接(传输数据)
Server端以20号端口去连接Client创建命令连接时使用的随机端口+1的端口
示例:
命令连接:Client(9527)–> Server(21),数据连接:Server(20/tcp) --> Client(9527+1)
2.被动模式
命令连接
Client以一个1024以上的随机端口号(端口号大于1023小于65535)来连接Server端的21号端口,命令连接建立完毕后,Server端会告知Client端用于创建数据连接的随机端口号(端口号大于1023小于65535)
数据连接
Client以创建命令连接时使用的随机端口号+1去连服务器端通过命令连接告知自己的一个随机端口号来创建数据连接
示例:
命令连接:Client(9527) --> Server(21),数据连接:Client(9527+1) --> Server(随机端口)
注意:
主动模式存在弊端,因为客户端的端口是随机的,客户端如果开了防火墙,则服务器端去连客户端创建数据连接时可能会被拒绝
用户认证
Ftp的用户认证主要有三种:
1.虚拟用户:仅用于访问某特定服务中的资源
虚拟用户通过ftp访问的资源位置为给虚拟用户指定的映射成为的系统用户的家目录
2.系统用户:可以登录系统的真实用户(出于对安全性的考虑,这种认证很少使用)
系统用户通过ftp访问的资源位置为用户的家目录
3.匿名用户
匿名用户(映射为ftp用户)的共享资源位置是/var/ftp
服务端
/etc/pam.d/vsftpd #vsftpd用户认证配置文件/etc/vsftpd/ #配置文件目录/etc/vsftpd/vsftpd.conf #主配置文件vsftpd常见的配置参数
客户端
lftp工具 支持指定用户名和密码登录
Vsftp安装与配置
在linux环境下,使用最多的FTP服务端软件就是Vsftpd!
安装Vsftpd,运行一下命令需要在root用户下进行!
- yum install vsftpd 安装
- yum remove vsftpd 卸载
复制代码 启动服务
启动服务 service vsftpd start 或者 systemctl start vsftpd.service
重启服务 systemctl restart vsftpd.service
停止服务 systemctl stop vsftpd.service
设置开机自启动 systemctl enable vsftpd.service
配置文件说明
/etc/vsftpd/vsftpd.conf 这个文件是vsftpd服务的核心配置文件!
我们在修改配置文件的时候,最好先备份一份!
cp /etc/vsftpd/vsftpd.conf /etc/vsftpd/vsftpd.conf.bak
/etc/vsftpd/ftpusers这个文件是禁止使用vsftpd的用户列表文件。记录不允许访问FTP服务器的用户名单,管理员可以把一些对系统安全有威胁的用户账号记录在此文件中,以免用户从FTP登录后获得大于上传下载操作的权利,而对系统造成损坏。
/etc/vsftpd/user_list这个文件禁止或允许使用vsftpd的用户列表文件。这个文件中指定的用户缺省情况(即在/etc/vsftpd/vsftpd.conf中设置userlist_deny=YES)下也不能访问FTP服务器,在设置了userlist_deny=NO时,仅允许user_list中指定的用户访问FTP服务器。
注意配置文件中的值一定不要有空格
传输模式配置
开启被动模式:
- connect_from_port_20=NO(默认为YES) #设置是否允许主动模式
- pasv_enable=YES(默认为YES) #设置是否允许被动模式
- pasv_min_port=50000(default:0(use any port))
- pasv_max_port=60000(default:0(use any port))
复制代码开启主动模式:
- connect_from_port_20=YES
- pasv_enable=NO
复制代码 系统用户配置
linux创建用户:
- useradd -d /home/ftp/compass -s /sbin/nologin compass
- passwd compass
复制代码为虚拟用户创建家目录
- mkdir -p /home/ftp/compass
复制代码设置权限
- chown -R compass:compass /home/ftp/compass
复制代码配置selinux允许ftp访问home和外网访问
- [root@master ~]# setsebool -P allow_ftpd_full_access on
- [root@master ~]# setsebool -P tftp_home_dir on
复制代码重启vsftpd服务
- [root@master ~]# systemctl restart vsftpd
复制代码无法登录的情况 在/etc/vsftpd/vsftpd.conf配置文件中添加了以下两句
- chroot_local_user=YES #原本就有,取掉注释就好
- allow_writeable_chroot=YES #添加
- chown compass:compass -R /home/ftp/compass
复制代码 java操作ftp文件服务器
有时候,我们需要接收用户上传的文件存储到服务器,然后保存起来,下次用户可以直接下载,或者是保存为档案,我这里提供三个方法,一个是上传文件到ftp,还有就是从ftp下载文件,还有就是删除ftp服务器删的文件
1.引入依赖
- <!-- ftp上传下载-->
- <dependency>
- <groupId>commons-net</groupId>
- <artifactId>commons-net</artifactId>
- <version>3.7</version>
- </dependency>
复制代码 2.提供接口
- import javax.servlet.http.HttpServletResponse;
- import java.io.InputStream;
- import java.io.OutputStream;
- /**
- * ftp文件服务器操作接口
- *
- * @author compass
- * @date 2022/10/19 15:00
- * @since 1.0.0
- **/
- public interface FtpService {
- /**
- * 上传文件到ftp
- *
- * @param inputStream 输入流
- * @param fileName 新的文件名,包含拓展名
- * @param filePath 保存路径
- * @return
- * @author compass
- */
- Boolean uploadFile(InputStream inputStream, String fileName, String filePath);
- /**
- * 下载ftp文件,直接转到输出流
- * @author compass
- * @param ftpFilePath
- * @author compass
- */
- byte[] downloadFileBytes(String ftpFilePath, HttpServletResponse response);
- /**
- * 删除ftp文件
- * @author compass
- * @param ftpFilePath ftp下文件路径,根目录开始
- * @return
- */
- Boolean deleteFile(String ftpFilePath);
- }
复制代码 3.对提供操作ftp接口进行实现
- /**
- * @author compass
- * @date 2022-10-19
- * @since 1.0
- **/
- import java.io.*;
- import compass.token.pocket.com.common.AjaxResult;
- import compass.token.pocket.com.entity.FtpInstanceEntity;
- import compass.token.pocket.com.service.FtpService;
- import compass.token.pocket.com.utils.ResponseUtil;
- import lombok.extern.slf4j.Slf4j;
- import org.apache.commons.lang3.StringUtils;
- import org.apache.commons.net.ftp.FTP;
- import org.apache.commons.net.ftp.FTPClient;
- import org.apache.commons.net.ftp.FTPReply;
- import org.springframework.stereotype.Service;
- import javax.annotation.Resource;
- import javax.servlet.http.HttpServletResponse;
- @Slf4j
- @Service
- public class FtpServiceImpl implements FtpService {
- @Resource
- FtpInstanceEntity ftpInstanceEntity;
- /**
- * 上传文件到ftp
- *
- * @param inputStream 输入流
- * @param fileName 新的文件名,包含拓展名
- * @param filePath 保存路径
- * @return
- * @author compass
- */
- @Override
- public Boolean uploadFile(InputStream inputStream, String fileName, String filePath) {
- // 定义保存结果
- boolean isSuccess = false;
- // 初始化连接
- FTPClient ftp = connectFtpServer();
- if (ftp != null) {
- try {
- // 设置文件传输模式为二进制,可以保证传输的内容不会被改变
- ftp.setFileType(FTP.BINARY_FILE_TYPE);
- //注:上传文件都为0字节,设置为被动模式即可
- ftp.enterLocalPassiveMode();
- // 跳转到指定路径,逐级跳转,不存在的话就创建再进入
- toPathOrCreateDir(ftp, filePath);
- // 上传文件 参数:上传后的文件名,输入流,,返回Boolean类型,上传成功返回true
- isSuccess = ftp.storeFile(fileName, inputStream);
- // 关闭输入流
- inputStream.close();
- // 退出ftp
- ftp.logout();
- } catch (IOException e) {
- log.error(e.toString());
- } finally {
- if (ftp.isConnected()) {
- try {
- // 断开ftp的连接
- ftp.disconnect();
- } catch (IOException ioe) {
- log.error(ioe.toString());
- }
- }
- }
- }
- return isSuccess;
- }
- /**
- * 下载ftp文件,直接转到输出流
- *
- * @param ftpFilePath
- * @author compass
- */
- @Override
- public byte[] downloadFileBytes(String ftpFilePath , HttpServletResponse response) {
- FTPClient ftp = connectFtpServer();
- try {
- ByteArrayOutputStream stream = new ByteArrayOutputStream();
- ftp.setFileType(FTPClient.BINARY_FILE_TYPE);
- ftp.enterLocalPassiveMode();
- boolean retrieveFile = ftp.retrieveFile(ftpFilePath, stream);
- if (!retrieveFile){
- throw new RuntimeException("FTP文件下载失败");
- }
- ftp.logout();
- return stream.toByteArray();
- } catch (Exception e) {
- log.error("FTP文件下载失败!" + e.toString());
- ResponseUtil.response(response,AjaxResult.businessError(e.getMessage(), "-1"));
- throw new RuntimeException(e.getMessage());
- } finally {
- if (ftp.isConnected()) {
- try {
- ftp.disconnect();
- } catch (IOException ioe) {
- log.error(ioe.toString());
- }
- }
- }
- }
- /**
- * 删除ftp文件
- * @param ftpFilePath 文件路径
- * @return java.lang.Boolean
- * @author compass
- * @date 2022/10/19 18:35
- * @since 1.0.0
- **/
- @Override
- public Boolean deleteFile(String ftpFilePath ) {
- FTPClient ftp = connectFtpServer();
- boolean result = false;
- try {
- result = ftp.deleteFile(ftpFilePath);
- ftp.logout();
- return result;
- } catch (Exception e) {
- log.error("FTP文件删除失败!" + e.toString());
- } finally {
- if (ftp.isConnected()) {
- try {
- ftp.disconnect();
- } catch (IOException ioe) {
- log.error(ioe.toString());
- }
- }
- }
- return result;
- }
- /**
- * 创建FTPClient对象
- *
- * @return org.apache.commons.net.ftp.FTPClient
- * @author compass
- * @date 2022/10/19 18:35
- * @since 1.0.0
- **/
- private FTPClient connectFtpServer() {
- // 创建FTPClient对象(对于连接ftp服务器,以及上传和上传都必须要用到一个对象)
- FTPClient ftpClient = new FTPClient();
- // 设置连接超时时间
- ftpClient.setConnectTimeout(1000 * 10);
- // 设置ftp字符集
- ftpClient.setControlEncoding("utf-8");
- // 设置被动模式,文件传输端口设置,否则文件上传不成功,也不报错
- ftpClient.enterLocalPassiveMode();
- try {
- // 定义返回的状态码
- int replyCode;
- // 连接ftp(当前项目所部署的服务器和ftp服务器之间可以相互通讯,表示连接成功)
- ftpClient.connect(ftpInstanceEntity.getHost());
- // 输入账号和密码进行登录
- ftpClient.login(ftpInstanceEntity.getUsername(), ftpInstanceEntity.getPassword());
- // 接受状态码(如果成功,返回230,如果失败返回503)
- replyCode = ftpClient.getReplyCode();
- // 根据状态码检测ftp的连接,调用isPositiveCompletion(reply)-->如果连接成功返回true,否则返回false
- if (!FTPReply.isPositiveCompletion(replyCode)) {
- log.info("connect ftp {} failed", ftpInstanceEntity.getHost());
- // 说明连接失败,需要断开连接
- ftpClient.disconnect();
- throw new RuntimeException(String.format("connect ftp %s failed", ftpInstanceEntity.getHost()));
- }
- log.info("连接状态码:" + replyCode);
- } catch (IOException e) {
- log.error("connect fail:" + e.toString());
- throw new RuntimeException(String.format("connect ftp %s error", ftpInstanceEntity.getHost()));
- }
- return ftpClient;
- }
- /**
- * 跳转到指定路径 不存在就创建
- *
- * @param ftp 操作ftp对象
- * @param filePath 文件路径
- * @return void
- * @author compass
- * @date 2022/10/19 15:05
- * @since 1.0.0
- **/
- private void toPathOrCreateDir(FTPClient ftp, String filePath) throws IOException {
- String[] dirs = filePath.split("/");
- for (String dir : dirs) {
- if (StringUtils.isEmpty(dir)) {
- continue;
- }
- if (!ftp.changeWorkingDirectory(dir)) {
- ftp.makeDirectory(dir);
- ftp.changeWorkingDirectory(dir);
- }
- }
- }
- }
复制代码 4.配置ftp相关参数
在配置文件中配置连接ftp服务器的配置
- ftp:
- #ftp服务器的地址
- host: 127.0.0.1
- #ftp服务器的端口号(连接端口号)
- port: 21
- #ftp的用户名
- username: compass
- #ftp的密码
- password: 6317738ef8324199a24602308f3a9a18
- #ftp上传的根目录
- basePath: /home/ftp/compass
- #回显地址
- httpPath: ftp://127.0.0.1
复制代码然后将配置文件的内容读取到一个bean里面
- import lombok.Data;
- import org.springframework.boot.context.properties.ConfigurationProperties;
- import org.springframework.context.annotation.Configuration;
- @Data
- @Configuration
- @ConfigurationProperties(prefix = "ftp")
- public class FtpInstanceEntity {
- /**
- * ftp服务器的地址
- */
- private String host;
- /**
- * ftp服务器的端口号(连接端口号)
- */
- private String port;
- /**
- * ftp的用户名
- */
- private String username;
- /**
- * ftp的密码
- */
- private String password;
- /**
- * ftp上传的根目录
- */
- private String basePath;
- /**
- * 回显地址
- */
- private String httpPath;
- }
复制代码 5.写测试controller
- package compass.token.pocket.com.controller;
- import cn.hutool.core.date.DateUtil;
- import cn.hutool.core.util.IdUtil;
- import compass.token.pocket.com.common.AjaxResult;
- import compass.token.pocket.com.entity.FtpInstanceEntity;
- import compass.token.pocket.com.service.FtpService;
- import compass.token.pocket.com.utils.FileUtils;
- import compass.token.pocket.com.utils.ResponseUtil;
- import lombok.extern.slf4j.Slf4j;
- import org.apache.commons.io.FilenameUtils;
- import org.apache.commons.lang3.StringUtils;
- import org.springframework.web.bind.annotation.*;
- import org.springframework.web.multipart.MultipartFile;
- import javax.annotation.Resource;
- import javax.servlet.ServletOutputStream;
- import javax.servlet.http.HttpServletRequest;
- import javax.servlet.http.HttpServletResponse;
- import java.io.*;
- import java.net.URLEncoder;
- import java.util.Date;
- import java.util.HashMap;
- import java.util.Map;
- /**
- * 文件测试控制器
- * @author compass
- * @date 2022/10/27 22:41
- * @since 1.0.0
- **/
- @Slf4j
- @RestController
- @RequestMapping("/fileTestController")
- public class FileTestController {
- @Resource
- private FtpService ftpService;
- @Resource
- FtpInstanceEntity ftpInstanceEntity;
- /**
- * ftp服务器文件上传
- * @param file 需要上传的文件
- * @return compass.token.pocket.com.common.AjaxResult<java.lang.String>
- * @author compass
- * @date 2022/10/19 14:51
- * @since 1.0.0
- **/
- @PostMapping(value = "/pdfUpload")
- public AjaxResult<String> pdfUpload(@RequestParam("file") MultipartFile file) {
- try {
- String fileDir = DateUtil.format(new Date(), "yyyy-MM-dd");
- boolean upload = ftpService.uploadFile(file.getInputStream(), file.getOriginalFilename(), fileDir);
- if (upload) {
- String savePath = ftpInstanceEntity.getBasePath() + "/" + fileDir + "/" + file.getOriginalFilename();
- return AjaxResult.success("上传成功", savePath);
- } else {
- return new AjaxResult<>("file upload error");
- }
- } catch (IOException e) {
- e.printStackTrace();
- }
- return new AjaxResult<>("file upload error");
- }
- /**
- * ftp服务器文件下载
- * @param ftpPath 需要下载的文件路径
- * @return compass.token.pocket.com.common.AjaxResult<java.lang.String>
- * @author compass
- * @date 2022/10/19 14:51
- * @since 1.0.0
- **/
- @GetMapping(value = "/pdfDownload", produces = "application/octet-stream")
- public void downloadFile(@RequestParam("ftpPath") String ftpPath, HttpServletRequest request, HttpServletResponse response) {
- String userAgent = request.getHeader("User-Agent");
- String fileName = ftpPath.substring(ftpPath.lastIndexOf('/') + 1);
- try {
- if (userAgent.contains("MSIE") || userAgent.contains("Trident")) {
- //IE浏览器处理
- fileName = URLEncoder.encode(fileName, "UTF-8");
- } else {
- // 非IE浏览器的处理:
- fileName = new String(fileName.getBytes("UTF-8"), "ISO-8859-1");
- }
- response.setHeader("Content-Disposition", "attachment; filename=" + fileName);
- response.setContentType("application/octet-stream; charset=UTF-8");
- byte[] bytes = ftpService.downloadFileBytes( ftpPath,response);
- ServletOutputStream outputStream = response.getOutputStream();
- outputStream.write(bytes);
- outputStream.flush();
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
- /**
- * ftp服务器文件删除
- * @param ftpPath 需要删除的文件路径
- * @return compass.token.pocket.com.common.AjaxResult<java.lang.Boolean>
- * @author compass
- * @date 2022/10/19 14:51
- * @since 1.0.0
- **/
- @PostMapping("ftpDeleteFile")
- public AjaxResult<Boolean> ftpDeleteFile(@RequestParam("ftpPath") String ftpPath){
- Boolean isSuccess = ftpService.deleteFile(ftpPath);
- if (isSuccess){
- return AjaxResult.success("删除成功", true);
- }else {
- return AjaxResult.businessError("删除失败", false);
- }
- }
- }
复制代码到这里操作ftp文件服务器的基本操作差不多结束了,讲的不是特别详细,但是基本来说,够用,如果遇到不能解决的问题请自行百度。
以上就是Java操作FTP实现上传下载功能的详细内容,更多关于Java FTP上传下载的资料请关注中国红客联盟其它相关文章!