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

2018-02-09 12:51:43来源:oschina作者:君千殇520人点击

分享

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


vsftp简介

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


CentOS6.5安装vsftp

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


1.检测是否安装了vsftp

[html]view plaincopy


[root@dev04~]#rpm-qa|grepvsftpd
vsftpd-2.2.2-14.el6.x86_64

我是安装过的, 命令下方返回的是我的vsftp版本


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

[html]view plaincopy


rpm-evsftpd

再执行[html]view plaincopy


rpm-qa|grepvsftpd

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


2.安装vsftp

[html]view plaincopy


yuminstallvsftpd*-y 3.设置开机自启vsftp ftp服务

[html]view plaincopy


chkconfigvsftpdon 4.启动ftp服务

[html]view plaincopy


[root@dev04vsftpd]#servicevsftpdstart

关闭服务:[html]view plaincopy


[root@dev04vsftpd]#servicevsftpdstop

重启服务: [html]view plaincopy


[root@dev04vsftpd]#servicevsftpdrestart
5.配置vsftp的配置文件vsftpd.conf

在 /etc/vsftpd目录下有三个文件和一个执行命令?[html]view plaincopy


-rw-------1rootroot125Jul242015ftpusers
-rw-------1rootroot371Apr614:11user_list
-rw-------1rootroot4737Apr115:51vsftpd.conf
-rwxr--r--1rootroot338Jul242015vsftpd_conf_migrate.sh

要修改的配置信息都在vsftpd.conf配置文件中,修改配置文件之前最好进行备份


[html]view plaincopy


[root@dev04vsftpd]#cpvsftpd.confvsftpd.conf.bak

再进行编辑修改配置文件[html]view plaincopy


[root@dev04vsftpd]#vim/etc/vsftpd/vsftpd.conf
主要的就是匿名用户的访问权限和 新增本地用户设置访问权限

因为要提供给终端使用FTP服务器映射的公网进行访问,又不行暴露服务器文件所在的目录,


必须支持匿名用户的访问, ftp匿名用户默认目录为 /var/ftp/pub目录下


vsftpd.conf配置文件主要修改


[html]view plaincopy


anonymous_enable=YES//支持匿名登录,如果改为NO不支持匿名登录

[html]view plaincopy


write_enable=YES//支持读

[html]view plaincopy


anon_upload_enable=YES//支持匿名改写

[html]view plaincopy


anon_mkdir_write_enable=YES//支持匿名用户创建子目录
6.创建用户ftpuser,指定目录/home/ftpuser,禁止SSH登录权限

[html]view plaincopy


useradd-d/home/ftpuser-gftp-s/sbin/nologinftpuser

给帐号设置密码[html]view plaincopy


passwdftpuer

注意:在设置密码时会提示什么密码过于简单,或者密码为单词词组等等,不用管,它已经进行设置了,要确认两次输入的密码都相同;输入两次密码后出现successful.....就代表设置密码成功了;


创建用户时遇到的问题:

1.我开始是关闭匿名用户的登录权限的,设置了ftpuser, 终端访问时必须要输入 ftp://username:password@IP地址:port/访问路径这样会暴露文件存放的路径,不合理(我觉得可能是我设置ftpuser用户时没有在配置文件中配置默认目录什么的,因为按照前面这样访问时候默认进入默认目录/ftpuser的,这点没怎么清楚==)


2.项目中上传文件原本是使用fastDFS文件服务器的,现在改为FTP,将固件上传到匿名用户目录下,提供终端下载


新建了anon用户将默认目录设置为匿名访问的目录 /var/ftp/pub

[html]view plaincopy


useradd-d/var/ftp-gftp-s/sbin/nologinanon

在使用java进行上传文件时发现anon用户不能连接上该目录,查资料说是没有对目录进行设置权限


LINUX下搭建FTP服务器--非宁静无以致远


注意的事项是的:


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


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


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

最后重启服务: service vsftpd restart 附上MultipartFile使用ftp上传文件
1.maven项目中添加pom文件

[html]view plaincopy




commons-net
commons-net
3.6

2.ftp配置文件 ftp_client.properties

[html]view plaincopy


#公网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类,提高代码扩展性

[java]view plaincopy


@Component
publicclassFTPConfig{/**FTP服务器IP地址*/
@Value("#{configProperties['ftp_host']}")
privateStringfTPhost="localhost";/**FTP服务器端口*/
@Value("#{configProperties['ftp_port']}")
privateintftpPort=21;/**FTP服务器用户名*/
@Value("#{configProperties['ftp_user_name']}")
privateStringftpUserName;/**FTP用户密码*/
@Value("#{configProperties['ftp_user_pwd']}")
privateStringftpUserPasswd;/**用户远程上传文件存储目录*/
@Value("#{configProperties['remoteFile_addr']}")
privateStringremoteFileAddr;/**FTP公网映射地址*/
@Value("#{configProperties['ftp_public_network_add']}")
privateStringftpNetAdd;/**FTP公网端口*/
@Value("#{configProperties['ftp_public_network_port']}")
privateintftpNetPort=2121;

[java]view plaincopy


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

[java]view plaincopy


/**
*@TypeftpClientFileManager.java
*@Desc
*@author123
*@date2017年3月31日下午2:50:58
*@version
*/
@Service
publicclassFTPFileManager{privatestaticfinalLoggerlogger=LoggerFactory.getLogger(FTPFileManager.class);privateFTPClientftpClient=newFTPClient();/**路径分隔符*/
privatestaticfinalStringSEPARATOR="/";/**FTP访问路径前缀*/
privatestaticfinalStringFTP_PREFIX="ftp://";@Autowired
privateFTPConfigconfig;/**
*@Title:upload
*
*@Description:文件上传
*
*@paramuploadFile
*MultipartFile
*@return
*返回保存路径+文件名
*@throwsException
*
*/
publicStringupload(MultipartFileuploadFile)throwsException{ftpClient=newFTPClient();
ftpClient.connect(config.getfTPhost(),config.getFtpPort());ftpClient.login(config.getFtpUserName(),config.getFtpUserPasswd());
//设置ftp字节流
ftpClient.setFileType(FTPClient.BINARY_FILE_TYPE);
intreply;
reply=ftpClient.getReplyCode();
if(!FTPReply.isPositiveCompletion(reply)){
ftpClient.disconnect();
returnnull;
}
ftpClient.enterLocalPassiveMode();
StringbasePath=config.getRemoteFileAddr();
ftpClient.changeWorkingDirectory(basePath);//为当天日期文件创建追加目录
Datetoday=newDate();
SimpleDateFormatsdf=newSimpleDateFormat("yyyy/MM/dd");
StringappendFile=sdf.format(today);
//切换到上传目录
if(!ftpClient.changeWorkingDirectory(basePath+appendFile)){
//如果目录不存在创建目录
String[]dirs=appendFile.split(SEPARATOR);
StringtempTargetPath=basePath;
for(Stringdir:dirs){
if(null==dir||"".equals(dir))
continue;
tempTargetPath+=SEPARATOR+dir;
if(!ftpClient.changeWorkingDirectory(tempTargetPath)){
if(!ftpClient.makeDirectory(tempTargetPath)){
logger.error("ftp文件目录创建失败!");
returnnull;
}else{
ftpClient.changeWorkingDirectory(tempTargetPath);
}
}
}
}StringfileName=newString(uploadFile.getOriginalFilename().getBytes("utf-8"),
"iso-8859-1");
/**
*避免文件过多后出现重名,设置30位随机数为文件名称
*/
//获取文件扩展名形如:.txt
StringfileExt=fileName.substring(fileName.lastIndexOf("."));
StringnewFileName=RandomUtils.randomAlphanumericStrictly(30)+fileExt;InputStreamis=uploadFile.getInputStream();
if(!ftpClient.storeFile(newFileName,is)){
logger.error("文件{}存储过程出错",fileName);
returnnull;
}
is.close();
ftpClient.disconnect();
/*StringfinalFile=basePath+SEPARATOR+appendFile+SEPARATOR+fileName;*/
//直接存储为匿名用户可直接访问下载的地址
StringfinalFile=FTP_PREFIX+config.getFtpNetAdd()+":"+config.getFtpNetPort()
+SEPARATOR+"pub"+SEPARATOR+appendFile+SEPARATOR+newFileName;
logger.info("文件上传FTP服务器成功,访问文件路径为:"+finalFile);
returnfinalFile;
}/**
*从FTP服务器上下载文件
*@paramremote远程文件路径
*@paramlocal本地文件路径
*@return是否成功
*@throwsException
*/
publicbooleandownload(Stringremote,Stringlocal)throwsException{
ftpClient=newFTPClient();
ftpClient.connect(config.getfTPhost(),config.getFtpPort());
ftpClient.login(config.getFtpUserName(),config.getFtpUserPasswd());
ftpClient.setFileType(FTPClient.BINARY_FILE_TYPE);
intreply;
reply=ftpClient.getReplyCode();
if(!FTPReply.isPositiveCompletion(reply)){
ftpClient.disconnect();
returnfalse;
}ftpClient.enterLocalPassiveMode();
ftpClient.setFileType(FTPClient.BINARY_FILE_TYPE);
booleanresult;
Filef=newFile(local);
FTPFile[]files=ftpClient.listFiles(remote);
if(files.length!=1){
System.out.println("远程文件不唯一");
ftpClient.disconnect();
returnfalse;
}
longlRemoteSize=files[0].getSize();
if(f.exists()){
OutputStreamout=newFileOutputStream(f,true);
System.out.println("本地文件大小为:"+f.length());
if(f.length()>=lRemoteSize){
logger.error("本地文件大小大于远程文件大小,下载中止");
out.close();
ftpClient.disconnect();
returnfalse;
}
ftpClient.setRestartOffset(f.length());
result=ftpClient.retrieveFile(remote,out);
out.close();
}else{
OutputStreamout=newFileOutputStream(f);
result=ftpClient.retrieveFile(remote,out);
out.close();
}
ftpClient.disconnect();
returnresult;
}}/**
*Revisionhistory
*-------------------------------------------------------------------------
*
*DateAuthorNote
*-------------------------------------------------------------------------
*2017年3月31日123creat
*/

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


简单的记录.


+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 支持断点续传的上传方法 [java]view plaincopy


/**
*上传文件到FTP服务器,支持断点续传
*@paramlocal本地文件名称,绝对路径
*@paramremote远程文件路径,使用/home/directory1/subdirectory/file.ext按照Linux上的路径指定方式,支持多级目录嵌套,支持递归创建不存在的目录结构
*@return上传结果
*@throwsIOException
*/
publicStringupload(Stringlocal,Stringremote)throwsIOException{
//设置PassiveMode传输
ftpClient.enterLocalPassiveMode();
//设置以二进制流的方式传输
ftpClient.setFileType(FTP.BINARY_FILE_TYPE);
StringuploadStatus;
//对远程目录的处理
StringremoteFileName=remote;
if(remote.contains("/")){
remoteFileName=remote.substring(remote.lastIndexOf("/")+1);
Stringdirectory=remote.substring(0,remote.lastIndexOf("/")+1);
if(!directory.equalsIgnoreCase("/")&&!ftpClient.changeWorkingDirectory(directory)){
//如果远程目录不存在,则递归创建远程服务器目录
intstart=0;
intend=0;
if(directory.startsWith("/")){
start=1;
}else{
start=0;
}
end=directory.indexOf("/",start);
while(true){
StringsubDirectory=remote.substring(start,end);
if(!ftpClient.changeWorkingDirectory(subDirectory)){
if(ftpClient.makeDirectory(subDirectory)){
ftpClient.changeWorkingDirectory(subDirectory);
}else{
System.out.println("创建目录失败");
returnUploadStatus.Create_Directory_Fail;
}
}start=end+1;
end=directory.indexOf("/",start);//检查所有目录是否创建完毕
if(end<=start){
break;
}
}
}
}//检查远程是否存在文件
FTPFile[]files=ftpClient.listFiles(remoteFileName);
if(files.length==1){
longremoteSize=files[0].getSize();
Filef=newFile(local);
longlocalSize=f.length();
if(remoteSize==localSize){
returnUploadStatus.File_Exits;
}elseif(remoteSize>localSize){
returnUploadStatus.Remote_Bigger_Local;
}//尝试移动文件内读取指针,实现断点续传
InputStreamis=newFileInputStream(f);
if(is.skip(remoteSize)==remoteSize){
//设置文件开始
ftpClient.setRestartOffset(remoteSize);
if(ftpClient.storeFile(remote,is)){
is.close();
returnUploadStatus.Upload_From_Break_Success;
}}//如果断点续传没有成功,则删除服务器上文件,重新上传
if(!ftpClient.deleteFile(remoteFileName)){
returnUploadStatus.Delete_Remote_Faild;
}
is=newFileInputStream(f);
if(ftpClient.storeFile(remote,is)){
uploadStatus=UploadStatus.Upload_New_File_Success;
}else{
uploadStatus=UploadStatus.Upload_New_File_Failed;
}
is.close();
}else{
InputStreamis=newFileInputStream(local);
if(ftpClient.storeFile(remoteFileName,is)){
uploadStatus=UploadStatus.Upload_New_File_Success;
}else{
uploadStatus=UploadStatus.Upload_New_File_Failed;
}
is.close();
}
returnuploadStatus;
}

微信扫一扫

第七城市微信公众平台