设为首页 收藏本站
查看: 3477|回复: 0

[经验分享] 基于Ftp/Sftp协议的文件服务器数据传输

[复制链接]

尚未签到

发表于 2016-6-10 09:21:59 | 显示全部楼层 |阅读模式
  在项目中将应用服务器和文件服务器分开,能够增加项目的可维护性。本例分别使用FTP协议和SFTP协议在Windows Server 2008和Linux系统实现对文件的上传、下载和删除操作。
  服务接口如下:

public interface FileManageDao {
/**
* 上传文件
* @param inputStream:文件输入流
* @param fileName:上传文件名称
*/
public void upload(InputStream inputStream,String directory,String fileName) ;
/**
* 下载文件
* @param outputStream:文件输出流
* @param fileName:下载文件名称
*/
public void download(OutputStream outputStream,String directory,String fileName) ;
/**
* 获取文件的输入流
* @param directory
* @param fileName
* @return
*/
public InputStream getAttachmentFile(String directory,String fileName);
/**
* 根据文档全名删除文件
* @param fileName
*/
public void delete(String directory,String fileName) ;
/**
* 判断文件是否存在
* @param directory
* @param fileName
* @return
*/
public boolean isExist(String directory,String fileName) ;
}
  服务接口模板类:

public abstract class FileManageDaoTemplate implements FileManageDao {
//主机ip
public String host="" ;
//端口号,默认为22
public int port = 22 ;
//服务器用户名,默认为root
public String userName="root" ;
//服务器密码
public String password ;
//服务器上传地址
public String targetBaseLocation = "chrhc" ;
//服务器连接超时时间(ms),默认60000
public int timeout = 60000;
public void setTargetBaseLocation(String targetBaseLocation) {
if(!StringUtils.hasLength(targetBaseLocation)){
return ;
}
targetBaseLocation = StringUtils.trimLeadingCharacter(targetBaseLocation, '/') ;
targetBaseLocation = StringUtils.trimTrailingCharacter(targetBaseLocation, '/') ;
this.targetBaseLocation = targetBaseLocation ;
}
....set and get method.....
}
  下面在不同的系统分别采用不同的方式实现:
  1.Windows 2008 Server下FTP协议
  在Windows下选择使用FTP协议的原因是FTP是Windows自带的服务,只需要通过配置开启FTP服务即可,不需要第三方的安装包。Windows 2008下FTP配置见附件。
  使用第三方免费安装包freeftpd也可以实现Windows系统下的FTP、SSH和SFTP服务,但是需要在软件界面中做相应的配置。
  JAR包:使用Apache的FTP开源组件commons-net,添加如下Maven依赖:

  <dependency>
      <groupId>commons-net</groupId>
      <artifactId>commons-net</artifactId>
      <version>3.3</version>
  </dependency>
  文件上传模板类实现:



public class FileManageDaoImplByFTP extends FileManageDaoTemplate {
/**
* 获取ftp客户端连接
* @return
* @throws SocketException
* @throws IOException
*/
public FTPClient getFtpClient(String directory) throws SocketException, IOException{
FTPClient ftpClient = new FTPClient();
ftpClient.connect(host, port);
ftpClient.setConnectTimeout(timeout);
boolean loginFlag = ftpClient.login(userName, password) ;
if(!loginFlag){
throw new SocketException("ftp login error ,please check setting .") ;
}
//设置文件上传目录
if(StringUtils.hasLength(directory.trim())){
directory = targetBaseLocation+"/"+StringUtils.trimLeadingCharacter(directory, '/');
}else{
directory = targetBaseLocation ;
}
//设置上传目录
boolean flag = ftpClient.changeWorkingDirectory(directory) ;
//如果目录不存在,则创建目录
if(!flag){
String path = null ;
for (String folderName : directory.split("/")) {
if(path==null){
path = folderName ;
}else{
path += "/"+folderName ;
}
ftpClient.makeDirectory(path);
}
flag = ftpClient.changeWorkingDirectory(path) ;
}
ftpClient.setBufferSize(1024);
ftpClient.setControlEncoding("utf-8");
//设置文件类型(二进制)      
ftpClient.setFileType(FTPClient.BINARY_FILE_TYPE);
//设置ftp上传模式为client向server传
ftpClient.enterLocalPassiveMode();
return ftpClient ;
}
@Override
public void upload(InputStream inputStream,String directory, String fileName) {
FTPClient ftpClient = null ;
try {
ftpClient = getFtpClient(directory);
//文件名称使用数字+英文,使用汉字会有乱码
boolean flag = ftpClient.storeFile(new String(fileName.getBytes("utf-8"),"iso-8859-1"), inputStream);
if(!flag){
throw new Exception("上传文件 "+fileName+" 失败。") ;
}
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally{
//关闭连接
IOUtils.closeQuietly(inputStream);      
close(ftpClient);
}
}
@Override
public void download(OutputStream outputStream,String directory, String fileName){
FTPClient ftpClient = null ;
try {
ftpClient = getFtpClient(directory);
boolean fileIsExist = isExist(ftpClient, fileName) ;
if(fileIsExist){
boolean flag = ftpClient.retrieveFile(fileName, outputStream) ;
if(!flag){
throw new Exception("下载文件 "+fileName+" 失败。") ;
}
}else{
throw new Exception("FTP服务器文件 "+fileName+" 不存在.......") ;
}
} catch (Exception e) {
// TODO Auto-generated catch block
System.out.println(e.getMessage());
}finally{
close(ftpClient);   
}
}
@Override
public void delete(String directory, String fileName) {
FTPClient ftpClient = null ;
try {
ftpClient = getFtpClient(directory);
boolean flag = ftpClient.deleteFile(new String(fileName.getBytes("utf-8"),"iso-8859-1"));
if(!flag){
throw new Exception("删除文件 "+fileName+" 失败。") ;
}
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally{
//关闭连接
close(ftpClient);  
}
}
public InputStream getAttachmentFile(String directory,String fileName){
FTPClient ftpClient = null ;
InputStream inputStream = null;
try {
ftpClient = getFtpClient(directory);
boolean fileIsExist = isExist(ftpClient, fileName) ;
if(fileIsExist){
inputStream = ftpClient.retrieveFileStream(fileName);
}else{
throw new Exception("FTP服务器文件 "+fileName+" 不存在.......") ;
}
} catch (Exception e) {
// TODO Auto-generated catch block
System.out.println(e.getMessage());
}finally{
//关闭连接
close(ftpClient);  
}
return inputStream ;
}
public void close(FTPClient ftpClient){
if(ftpClient!=null){
try {
ftpClient.disconnect();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
/**
* 判断ftpClient当前所在目录下,名称为fileName的文件是否存在
* @param ftpClient
* @param fileName
* @return
*/
public boolean isExist(FTPClient ftpClient,final String fileName) {
boolean flag = false ;
try {
FTPFile[] ftpFiles = ftpClient.listFiles(null, new FTPFileFilter() {
@Override
public boolean accept(FTPFile file) {
if(file.getName().equals(fileName)){
return true ;
}
return false ;
}
}) ;
if(ftpFiles!=null&&ftpFiles.length>0){
flag = true ;
}
} catch (Exception e) {
System.out.println("-------query ftp file exist fail......");
}
return flag ;
}
@Override
public boolean isExist(String directory, final String fileName) {
boolean flag = false ;
FTPClient ftpClient = null ;
try {
ftpClient = getFtpClient(directory) ;
FTPFile[] ftpFiles = ftpClient.listFiles(null, new FTPFileFilter() {
@Override
public boolean accept(FTPFile file) {
if(file.getName().equals(fileName)){
return true ;
}
return false ;
}
}) ;
if(ftpFiles!=null&&ftpFiles.length>0){
flag = true ;
}
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally{
close(ftpClient);
}
return flag ;
}
}


注:使用FTP上传文件时,在有的机器上会停留在storeFile方法上不动,没有反应。解决:需要设置FTP数据连接模式为被动模式,即在获取FTP连接后设置ftpclient.enterLocalPassiveMode()。原因是:FTP协议有两种工作方式:PORT方式和PASV方式,中文意思为主动式和被动式。 PORT(主动)方式的连接过程是:客户端向服务器的FTP端口(默认是21)发送连接请 求,服务器接受连接,建立一条命令链路。当需要传送数据时,客户端在命令链路上用PORT 命令告诉服务器:“我打开了XXXX端口,你过来连接我”。于是服务器从20端口向客户端的 XXXX端口发送连接请求,建立一条数据链路来传送数据。 PASV(被动)方式的连接过程是:客户端向服务器的FTP端口(默认是21)发送连接请 求,服务器接受连接,建立一条命令链路。当需要传送数据时,服务器在命令链路上用PASV 命令告诉客户端:“我打开了XXXX端口,你过来连接我”。于是客户端向服务器的XXXX端口 发送连接请求,建立一条数据链路来传送数据。被动模式不需要关注客户端配置情况,只需要关注FTP服务器即可。FTP数据连接模式默认为本地主动模式,停留在storeFile方法上有可能是因为本地机器的某个端口被占用,所以没法传数据。


2.Linux下SFtp协议
  在Linux使用vsftp安装和配置文件服务器,经测试和使用比较稳定。Linux下配置vsftp参考:http://jingyan.baidu.com/article/adc815133476bdf723bf7393.html
  在Linux系统下使用SFTP协议传输文件,相比较FTP,SFTP协议对传输的文件进行了加密,使传输过程更加安全,但是由于使用了加密,传输速度要比FTP的传输速度慢。
  JAR包:使用JSCH(Java Sercure Channel),JSCH是一个纯粹的用java实现SSH功能的java  library。Maven依赖如下:

<dependency>
<groupId>com.jcraft</groupId>
<artifactId>jsch</artifactId>
<version>0.1.51</version>
</dependency>
 文件上传模板类实现:
public class FileManageDaoImplBySFTP extends FileManageDaoTemplate {
/**
* 获取连接通道
* @return
* @throws JSchException
*/
public ChannelSftp getChannel() throws JSchException {
JSch jsch = new JSch(); // 创建JSch对象
Session session = jsch.getSession(userName, host, port); // 根据用户名,主机ip,端口获取一个Session对象
if (password != null) {
session.setPassword(password); // 设置密码
}
Properties config = new Properties();
config.put("StrictHostKeyChecking", "no");
session.setConfig(config); // 为Session对象设置properties
session.setTimeout(timeout); // 设置timeout时间
session.connect(); // 通过Session建立链接
Channel channel = session.openChannel("sftp"); // 打开SFTP通道
channel.connect(); // 建立SFTP通道的连接
return (ChannelSftp) channel;
}
/**
* 关闭连接通道
* @throws Exception
*/
public void closeChannel(Channel channel) {
if(channel==null){
return ;
}
channel.disconnect();
try {
//关闭会话
if (channel.getSession() != null) {
channel.getSession().disconnect();
}
} catch (JSchException e) {
e.printStackTrace();
}
}
/**
* 上传文件到文件服务器
* @param inputStream
* @param fileName
* @throws JSchException
*/
@Override
public void upload(InputStream inputStream,String directory,String fileName) {
ChannelSftp channelSftp = null;
try {
channelSftp = getChannel();
channelSftp.put(inputStream, targetBaseLocation+directory+fileName);
channelSftp.quit();
} catch (Exception e) {
e.printStackTrace();
}finally{
closeChannel(channelSftp);
}
}
/**
* 从文件服务器上下载文件
*/
@Override
public void download(OutputStream outputStream,String directory,String fileName){
ChannelSftp channelSftp = null;
try {
channelSftp = getChannel();
channelSftp.get(targetBaseLocation+directory+fileName, outputStream, null,ChannelSftp.OVERWRITE , 0);
} catch (Exception e) {
//TODO Auto-generated catch block
e.printStackTrace();
}finally{
closeChannel(channelSftp);
}
}
public InputStream getAttachmentFile(String directory,String fileName){
return null ;
}
@Override
public void delete(String directory,String fileName) {
ChannelSftp channelSftp = null;
try {
channelSftp = getChannel();
channelSftp.rm(targetBaseLocation+directory+fileName);
} catch (Exception e) {
//TODO Auto-generated catch block
e.printStackTrace();
}finally{
closeChannel(channelSftp);
}
}
@Override
public boolean isExist(String directory, String fileName) {
// TODO Auto-generated method stub
return false;
}
}
  注:项目框架是Spring,DAO层的类都是使用单例模式,考虑到多线程,在DAO类中不要包括和连接相关的属性,以免一个线程在传输文件过程连接被另一个线程关闭。 
  Spring配置文件中配置模板类后,根据文件服务器的系统类型选择不同的传输方式。

<!-- 文件服务器模板方法 -->
<bean id="fileManageDaoTemplate" class="com.dao.FileManageDaoTemplate" abstract="true">
<property name="host" value="ip"></property>
<property name="port" value="port"></property>
<property name="userName" value="administrator"></property>
<property name="password" value="password"></property>
<property name="targetLocation" value="/directory/"></property>
<property name="timeout" value="60000"></property>
</bean>
<!-- linux系统使用sftp保存文件 -->
<!-- <bean class="com.dao.FileManageDaoImplBySFTP" parent="fileManageDaoTemplate"> -->
<!-- </bean> -->
<bean class="com.dao.FileManageDaoImplByFTP" parent="fileManageDaoTemplate"></bean>
  
  显示FTP服务器上的图片文件,方案如下:
  1.FTP服务上的图片不能直接通过ftp链接显示,在Controller层的一个方法中根据文件目录和文件名,下载下文件后,再将文件回传回去。简单实现如下:

@RequestMapping(value = "/showImage/{directory}/{filename}.{suffix}")
public void downloadImage(
@PathVariable("directory") String directory,
@PathVariable("filename") String filename,
@PathVariable("suffix") String suffix,
HttpServletResponse response) throws Exception {  
response.setContentType("image/jpeg");
attachmentFileService.get(response.getOutputStream(),directory+"/" , filename+"."+suffix) ;
}
  2.在ftp服务器的目录下搭建一个tomcat服务器,使用ftp上传时,将图片文件放到tomcat服务器下。前台通过访问ftp服务器下的tomcat服务器地址显示图片。
  



 

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-228492-1-1.html 上篇帖子: 如何修改默认的FTP帐号或密码 下篇帖子: lvs+keepalived+vsftp配置FTP服务器负载均衡
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表