CentOS6.5下搭建FTP服务器 + Java上传文件




项目中开始使用http协议进行传输文件,但是终端提出传输的字节超过限度,要使用FTP文件服务器进行断点续传,每次1k的进行传输. 老大让我在开发环境中搭建vsftp服务器,一起没操作过,现在记录下来留做以后的回顾学习;

vsftp简介

vsftpd是一款在 Linux发行版中最受推崇的FTP服务器程序。特点是小巧轻快,安全易用。ftp作为文件服务器,断点续传是和http传输的一个区别;

CentOS6.5安装vsftp

以管理员(root)身份进行一下操作:

1.检测是否安装了vsftp


   
   
  1. [root@dev04 ~]# rpm -qa |grep vsftpd
  2. vsftpd-2.2.2-14.el6.x86_64
我是安装过的, 命令下方返回的是我的vsftp版本

检测到安装过后如果想卸载执行:

rpm -e vsftpd
    
    
再执行
rpm -qa |grep vsftpd
    
    

查看卸载是否成功,如果成功会返回卸载的vsftp的版本

2.安装vsftp

yum install vsftpd* -y
   
   

3.设置开机自启vsftp ftp服务

chkconfig vsftpd on
    
    

4.启动ftp服务

[root@dev04 vsftpd]# service vsftpd start
     
     
关闭服务:
[root@dev04 vsftpd]# service vsftpd stop

     
     
重启服务:
[root@dev04 vsftpd]# service vsftpd restart
     
     

5.配置vsftp的配置文件vsftpd.conf

在 /etc/vsftpd目录下有三个文件和一个执行命令?


     
     
  1. -rw——- 1 root root 125 Jul 24 2015 ftpusers
  2. -rw——- 1 root root 371 Apr 6 14:11 user_list
  3. -rw——- 1 root root 4737 Apr 1 15:51 vsftpd.conf
  4. -rwxr–r– 1 root root 338 Jul 24 2015 vsftpd_conf_migrate.sh
要修改的配置信息都在vsftpd.conf配置文件中,修改配置文件之前最好进行备份
[root@dev04 vsftpd]# cp vsftpd.conf vsftpd.conf.bak
      
      

再进行编辑修改配置文件
[root@dev04 vsftpd]# vim /etc/vsftpd/vsftpd.conf

      
      

主要的就是匿名用户的访问权限和 新增本地用户设置访问权限

因为要提供给终端使用FTP服务器映射的公网进行访问,又不行暴露服务器文件所在的目录,
必须支持匿名用户的访问, ftp匿名用户默认目录为 /var/ftp/pub目录下
vsftpd.conf配置文件主要修改
anonymous_enable=YES   // 支持匿名登录,如果改为NO不支持匿名登录
      
      
write_enable=YES  //支持读

      
      
anon_upload_enable=YES   //支持匿名改写

      
      
anon_mkdir_write_enable=YES  //支持匿名用户创建子目录

      
      

6.创建用户ftpuser,指定目录/home/ftpuser,禁止SSH登录权限

useradd -d /home/ftpuser -g ftp -s /sbin/nologin ftpuser
       
       
给帐号设置密码
passwd ftpuer
       
       
注意: 在设置密码时会提示什么密码过于简单,或者密码为单词词组等等,不用管,它已经进行设置了,要确认两次输入的密码都相同;输入两次密码后出现successful…..就代表设置密码成功了;

创建用户时遇到的问题:

1.我开始是关闭匿名用户的登录权限的,设置了ftpuser, 终端访问时必须要输入 ftp://username:password@IP地址:port/访问路径   这样会暴露文件存放的路径,不合理(我觉得可能是我设置ftpuser用户时没有在配置文件中配置默认目录什么的,因为按照前面这样访问时候默认进入默认目录/ftpuser的,这点没怎么清楚==)
2.项目中上传文件原本是使用fastDFS文件服务器的,现在改为FTP,将固件上传到匿名用户目录下,提供终端下载
新建了anon用户将默认目录设置为匿名访问的目录 /var/ftp/pub

useradd -d /var/ftp -g ftp -s /sbin/nologin anon
       
       
在使用java进行上传文件时发现anon用户不能连接上该目录,查资料说是没有对目录进行设置权限

注意的事项是的:

1、pub目录的权限不能全开,全开会报错,chmod 755 -R pub

2、但是这样也会带来一个问题,当匿名用户登录后,在pub下的newftp目录下想上传一个文件,会报错,因为匿名用户(ftp)无法在该目录下(该目录属于root用户)创建一个新文件的,因为该目录下他没有权限,所以要加入其他用户(o)的写权限,chmod o+w /var/ftp/pub/.

现在权限变为:
drwxr-xrwx 2 root root   4096 06-20 16:55 pub
其实就是757的权限,此时匿名用户(ftp)就可以在该目录下上传文件了。


最后重启服务: service vsftpd restart


附上MultipartFile使用ftp上传文件

1.maven项目中添加pom文件


       
       
  1. <!-- https://mvnrepository.com/artifact/commons-net/commons-net -->
  2. <dependency>
  3. <groupId>commons-net </groupId>
  4. <artifactId>commons-net </artifactId>
  5. <version>3.6 </version>
  6. </dependency>

2.ftp配置文件 ftp_client.properties


       
       

#公网IP映射地址: 218.205.115.XXX 2121

ftp_public_network_add = 218.205.115.XXX
#FTP公网端口 ftp_public_network_port = 2121 #FTP服务器地址172.23.28.XXX ftp_host = 172.23.28.XXX #21 ftp_port = 21 ##匿名用户 anonymous 创建一个用户名为ftpuser,目录在pub下,具有增删改查/新建子目录权限 ftp_user_name = ftpuser ftp_user_pwd = ftpuser

匿名登录上传目录 /var/ftp/pub

remoteFile_addr = /var/ftp/pub/

3.自定义配置文件的pojo类,提高代码扩展性

  1. <code class=“language-html”>@Component  
  2. public class FTPConfig {  
  3.   
  4.     / FTP服务器IP地址 /  
  5.     @Value(“#{configProperties[‘ftp_host’]}”)  
  6.     private String fTPhost = “localhost”;  
  7.   
  8.     / FTP服务器端口 /  
  9.     @Value(“#{configProperties[‘ftp_port’]}”)  
  10.     private int ftpPort = 21;  
  11.   
  12.     / FTP服务器用户名 /  
  13.     @Value(“#{configProperties[‘ftp_user_name’]}”)  
  14.     private String ftpUserName;  
  15.   
  16.     / FTP用户密码 /  
  17.     @Value(“#{configProperties[‘ftp_user_pwd’]}”)  
  18.     private String ftpUserPasswd;  
  19.   
  20.     / 用户远程上传文件存储目录 /  
  21.     @Value(“#{configProperties[‘remoteFile_addr’]}”)  
  22.     private String remoteFileAddr;  
  23.   
  24.     / FTP公网映射地址 /  
  25.     @Value(“#{configProperties[‘ftp_public_network_add’]}”)  
  26.     private String ftpNetAdd;  
  27.   
  28.     / FTP公网端口 /  
  29.     @Value(“#{configProperties[‘ftp_public_network_port’]}”)  
  30.     private int ftpNetPort = 2121;</code>  

   
   
  1. @Component
  2. public class FTPConfig {
  3. / FTP服务器IP地址 /
  4. @Value("#{configProperties['ftp_host']}")
  5. private String fTPhost = "localhost";
  6. / * FTP服务器端口 /
  7. @Value("#{configProperties['ftp_port']}")
  8. private int ftpPort = 21;
  9. / * FTP服务器用户名 /
  10. @Value("#{configProperties['ftp_user_name']}")
  11. private String ftpUserName;
  12. / * FTP用户密码 /
  13. @Value("#{configProperties['ftp_user_pwd']}")
  14. private String ftpUserPasswd;
  15. / * 用户远程上传文件存储目录 /
  16. @Value("#{configProperties['remoteFile_addr']}")
  17. private String remoteFileAddr;
  18. / * FTP公网映射地址 /
  19. @Value("#{configProperties['ftp_public_network_add']}")
  20. private String ftpNetAdd;
  21. / * FTP公网端口 /
  22. @Value("#{configProperties['ftp_public_network_port']}")
  23. private int ftpNetPort = 2121;
}
  
  

4.FTP上传下载的工具类FTPFileManager


   
   
  1. /**
  2. * @Type ftpClientFileManager.java
  3. * @Desc
  4. * @author 123
  5. * @date 2017年3月31日 下午2:50:58
  6. * @version
  7. */
  8. @Service
  9. public class FTPFileManager {
  10. private static final Logger logger = LoggerFactory.getLogger(FTPFileManager.class);
  11. private FTPClient ftpClient = new FTPClient();
  12. /** 路径分隔符 */
  13. private static final String SEPARATOR = “/”;
  14. /** FTP访问路径前缀 */
  15. private static final String FTP_PREFIX = “ftp://”;
  16. @Autowired
  17. private FTPConfig config;
  18. /**
  19. * @Title: upload
  20. *
  21. * @Description: 文件上传
  22. *
  23. * @param uploadFile
  24. * MultipartFile
  25. * @return
  26. * 返回保存路径 + 文件名
  27. * @throws Exception
  28. *
  29. */
  30. public String upload(MultipartFile uploadFile) throws Exception {
  31. ftpClient = new FTPClient();
  32. ftpClient.connect(config.getfTPhost(), config.getFtpPort());
  33. ftpClient.login(config.getFtpUserName(), config.getFtpUserPasswd());
  34. //设置ftp字节流
  35. ftpClient.setFileType(FTPClient.BINARY_FILE_TYPE);
  36. int reply;
  37. reply = ftpClient.getReplyCode();
  38. if (!FTPReply.isPositiveCompletion(reply)) {
  39. ftpClient.disconnect();
  40. return null;
  41. }
  42. ftpClient.enterLocalPassiveMode();
  43. String basePath = config.getRemoteFileAddr();
  44. ftpClient.changeWorkingDirectory(basePath);
  45. //为当天日期文件创建追加目录
  46. Date today = new Date();
  47. SimpleDateFormat sdf = new SimpleDateFormat( “yyyy/MM/dd”);
  48. String appendFile = sdf.format(today);
  49. //切换到上传目录
  50. if (!ftpClient.changeWorkingDirectory(basePath + appendFile)) {
  51. //如果目录不存在创建目录
  52. String[] dirs = appendFile.split(SEPARATOR);
  53. String tempTargetPath = basePath;
  54. for (String dir : dirs) {
  55. if ( null == dir || “”.equals(dir))
  56. continue;
  57. tempTargetPath += SEPARATOR + dir;
  58. if (!ftpClient.changeWorkingDirectory(tempTargetPath)) {
  59. if (!ftpClient.makeDirectory(tempTargetPath)) {
  60. logger.error( “ftp文件目录创建失败!”);
  61. return null;
  62. } else {
  63. ftpClient.changeWorkingDirectory(tempTargetPath);
  64. }
  65. }
  66. }
  67. }
  68. String fileName = new String(uploadFile.getOriginalFilename().getBytes( “utf-8”),
  69. “iso-8859-1”);
  70. /**
  71. * 避免文件过多后出现重名,设置30位随机数为文件名称
  72. */
  73. // 获取文件扩展名 形如: .txt
  74. String fileExt = fileName.substring(fileName.lastIndexOf( “.”));
  75. String newFileName = RandomUtils.randomAlphanumericStrictly( 30) + fileExt;
  76. InputStream is = uploadFile.getInputStream();
  77. if (!ftpClient.storeFile(newFileName, is)) {
  78. logger.error( “文件{}存储过程出错”, fileName);
  79. return null;
  80. }
  81. is.close();
  82. ftpClient.disconnect();
  83. /* String finalFile = basePath + SEPARATOR + appendFile + SEPARATOR + fileName;*/
  84. //直接存储为匿名用户可直接访问下载的地址
  85. String finalFile = FTP_PREFIX + config.getFtpNetAdd() + “:” + config.getFtpNetPort()
  86. + SEPARATOR + “pub” + SEPARATOR + appendFile + SEPARATOR + newFileName;
  87. logger.info( “文件上传FTP服务器成功, 访问文件路径为 : “ + finalFile);
  88. return finalFile;
  89. }
  90. /**
  91. * 从FTP服务器上下载文件
  92. * @param remote 远程文件路径
  93. * @param local 本地文件路径
  94. * @return 是否成功
  95. * @throws Exception
  96. */
  97. public boolean download(String remote, String local) throws Exception {
  98. ftpClient = new FTPClient();
  99. ftpClient.connect(config.getfTPhost(), config.getFtpPort());
  100. ftpClient.login(config.getFtpUserName(), config.getFtpUserPasswd());
  101. ftpClient.setFileType(FTPClient.BINARY_FILE_TYPE);
  102. int reply;
  103. reply = ftpClient.getReplyCode();
  104. if (!FTPReply.isPositiveCompletion(reply)) {
  105. ftpClient.disconnect();
  106. return false;
  107. }
  108. ftpClient.enterLocalPassiveMode();
  109. ftpClient.setFileType(FTPClient.BINARY_FILE_TYPE);
  110. boolean result;
  111. File f = new File(local);
  112. FTPFile[] files = ftpClient.listFiles(remote);
  113. if (files.length != 1) {
  114. System.out.println( “远程文件不唯一”);
  115. ftpClient.disconnect();
  116. return false;
  117. }
  118. long lRemoteSize = files[ 0].getSize();
  119. if (f.exists()) {
  120. OutputStream out = new FileOutputStream(f, true);
  121. System.out.println( “本地文件大小为:” + f.length());
  122. if (f.length() >= lRemoteSize) {
  123. logger.error( “本地文件大小大于远程文件大小,下载中止”);
  124. out.close();
  125. ftpClient.disconnect();
  126. return false;
  127. }
  128. ftpClient.setRestartOffset(f.length());
  129. result = ftpClient.retrieveFile(remote, out);
  130. out.close();
  131. } else {
  132. OutputStream out = new FileOutputStream(f);
  133. result = ftpClient.retrieveFile(remote, out);
  134. out.close();
  135. }
  136. ftpClient.disconnect();
  137. return result;
  138. }
  139. }
  140. /**
  141. * Revision history
  142. * ————————————————————————-
  143. *
  144. * Date Author Note
  145. * ————————————————————————-
  146. * 2017年3月31日 123 creat
  147. */


此时,用户可以通过公网映射的IP进行访问, 类似 ftp://218.205.115.XXXX:2121/pub/2017/04/06/4cn91c2dRffmQ8diQ4mvDRo5m2lWtr.txt
由于项目中没使用到文件下载,我只在本地进行了简单的下载centOS中FTP文件测试.





简单的记录.


+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
支持断点续传的上传方法


  
  
  1. /*
  2. 上传文件到FTP服务器,支持断点续传
  3. * @param local 本地文件名称,绝对路径
  4. * @param remote 远程文件路径,使用/home/directory1/subdirectory/file.ext 按照Linux上的路径指定方式,支持多级目录嵌套,支持递归创建不存在的目录结构
  5. * @return 上传结果
  6. * @throws IOException
  7. */
  8. public String upload(String local, String remote) throws IOException {
  9. //设置PassiveMode传输
  10. ftpClient.enterLocalPassiveMode();
  11. //设置以二进制流的方式传输
  12. ftpClient.setFileType(FTP.BINARY_FILE_TYPE);
  13. String uploadStatus;
  14. //对远程目录的处理
  15. String remoteFileName = remote;
  16. if (remote.contains( "/")) {
  17. remoteFileName = remote.substring(remote.lastIndexOf( "/") + 1);
  18. String directory = remote.substring( 0, remote.lastIndexOf( "/") + 1);
  19. if (!directory.equalsIgnoreCase( "/") && !ftpClient.changeWorkingDirectory(directory)) {
  20. //如果远程目录不存在,则递归创建远程服务器目录
  21. int start = 0;
  22. int end = 0;
  23. if (directory.startsWith( "/")) {
  24. start = 1;
  25. } else {
  26. start = 0;
  27. }
  28. end = directory.indexOf( "/", start);
  29. while ( true) {
  30. String subDirectory = remote.substring(start, end);
  31. if (!ftpClient.changeWorkingDirectory(subDirectory)) {
  32. if (ftpClient.makeDirectory(subDirectory)) {
  33. ftpClient.changeWorkingDirectory(subDirectory);
  34. } else {
  35. System.out.println( "创建目录失败");
  36. return UploadStatus.Create_Directory_Fail;
  37. }
  38. }
  39. start = end + 1;
  40. end = directory.indexOf( "/", start);
  41. //检查所有目录是否创建完毕
  42. if (end <= start) {
  43. break;
  44. }
  45. }
  46. }
  47. }
  48. //检查远程是否存在文件
  49. FTPFile[] files = ftpClient.listFiles(remoteFileName);
  50. if (files.length == 1) {
  51. long remoteSize = files[ 0].getSize();
  52. File f = new File(local);
  53. long localSize = f.length();
  54. if (remoteSize == localSize) {
  55. return UploadStatus.File_Exits;
  56. } else if (remoteSize > localSize) {
  57. return UploadStatus.Remote_Bigger_Local;
  58. }
  59. //尝试移动文件内读取指针,实现断点续传
  60. InputStream is = new FileInputStream(f);
  61. if (is.skip(remoteSize) == remoteSize) {
  62. //设置文件开始
  63. ftpClient.setRestartOffset(remoteSize);
  64. if (ftpClient.storeFile(remote, is)) {
  65. is.close();
  66. return UploadStatus.Upload_From_Break_Success;
  67. }
  68. }
  69. //如果断点续传没有成功,则删除服务器上文件,重新上传
  70. if (!ftpClient.deleteFile(remoteFileName)) {
  71. return UploadStatus.Delete_Remote_Faild;
  72. }
  73. is = new FileInputStream(f);
  74. if (ftpClient.storeFile(remote, is)) {
  75. uploadStatus = UploadStatus.Upload_New_File_Success;
  76. } else {
  77. uploadStatus = UploadStatus.Upload_New_File_Failed;
  78. }
  79. is.close();
  80. } else {
  81. InputStream is = new FileInputStream(local);
  82. if (ftpClient.storeFile(remoteFileName, is)) {
  83. uploadStatus = UploadStatus.Upload_New_File_Success;
  84. } else {
  85. uploadStatus = UploadStatus.Upload_New_File_Failed;
  86. }
  87. is.close();
  88. }
  89. return uploadStatus;
  90. }
















猜你喜欢

转载自blog.csdn.net/qq_33384065/article/details/81201273