背景
对接欧洲一些外呼外包公司,特别是一些小国家的业务od公司,没有研发能力或者研发能力比较弱,在跨境电商场景,需要把审单、催签收等一些业务外包给他们,一般情况下是甲方把数据通过接口推送过去,然后乙方拿到业务数据进行外呼(包含ivr),把结果再通过接口回调传给甲方,但是乙方外呼系统是采购别人的,自己没有研发能力,所以数据交互需要通过离线的方式进行,按照对方的要求,需要我们搭建sftp作为数据中转站,我们定时把需要外呼跟进的数据通过文件的方式上传到sftp上,他们下载之后去外呼,然后把外呼跟进结果也以文件的方式上传到sftp服务器,我们定时去下载文件解析数据来做业务跟进。
一、安装Vsftpd
1.CentOs
代码语言:javascript复制yum -y install vsftpd
2.ubuntu
代码语言:javascript复制sudo apt-get install vsftpd
二、配置
1.vsftpd配置
代码语言:javascript复制[root@izwz94danzl2zfjfo5qll6z ~]# cat /etc/vsftpd/vsftpd.conf | grep -v "#"
anonymous_enable=NO
local_enable=YES
write_enable=YES
local_umask=022
dirmessage_enable=YES
xferlog_enable=YES
connect_from_port_20=YES
xferlog_file=/var/log/xferlog
xferlog_std_format=YES
chroot_local_user=YES
chroot_list_enable=NO
listen=NO
listen_ipv6=YES
pam_service_name=vsftpd
userlist_enable=YES
tcp_wrappers=YES
ssl_ciphers=HIGH
rsa_cert_file=/etc/ssl/certs/ssl-cert-snakeoil.pem
rsa_private_key_file=/etc/ssl/private/ssl-cert-snakeoil.key
ssl_enable=NO
2.修改防火墙配置
查看防火墙状态
代码语言:javascript复制getsebool -a | grep ftp
代码语言:javascript复制setsebool tftp_home_dir on
setsebool ftpd_full_access on
3.修改sshd_config
将
代码语言:javascript复制Subsystem sftp /usr/lib/openssh/sftp-server
替换为
代码语言:javascript复制Subsystem sftp internal-sftp
4.开放端口
根据协议开放对应的端口
三、创建账户
1.创建账户
代码语言:javascript复制useradd -d /home/ftpuser ftpuser
2.修改密码
代码语言:javascript复制passwrd ftpuser
3.修改主目录读写权限
代码语言:javascript复制chown -R ftpuser /home/ftpuser/
四、FileZilla访问
注意点:
- 协议要配置SFTP
- SFTP默认端口是22,FTP是21
五、Java客户端访问
1.Jsch方式
建立连接
代码语言:javascript复制 protected SftpConnect getConnect() throws Exception {
Session session ;
Channel channel ;
ChannelSftp sftp ;// sftp操作类
JSch jsch = new JSch();
session = jsch.getSession(username, hostname, port);
session.setPassword(password);
Properties config = new Properties();
config.put("StrictHostKeyChecking", "no"); // 不验证 HostKey
session.setConfig(config);
try {
session.connect();
} catch (Exception e) {
if (session.isConnected())
session.disconnect();
log.error("连接服务器失败,请检查主机[" hostname "],端口[" port
"],用户名[" username "],端口[" port
"]是否正确,以上信息正确的情况下请检查网络连接是否正常或者请求被防火墙拒绝.");
}
channel = session.openChannel("sftp");
try {
channel.connect();
} catch (Exception e) {
if (channel.isConnected())
channel.disconnect();
log.error("连接服务器失败,请检查主机[" hostname "],端口[" port
"],用户名[" username "],密码是否正确,以上信息正确的情况下请检查网络连接是否正常或者请求被防火墙拒绝.");
}
sftp = (ChannelSftp) channel;
return SftpConnect.builder()
.channel(channel)
.session(session)
.sftp(sftp)
.build();
}
上传文件
代码语言:javascript复制 public void upload(InputStream inputStream, String pathname, String fileName) throws Exception {
SftpConnect connect = this.getConnect();
ChannelSftp sftp = connect.getSftp();// sftp操作类
try {
sftp.cd(pathname);
sftp.put(inputStream,fileName);
} catch (Exception e) {
log.error("JSchProxy.upload occur error;pathname={},fileName={}",pathname,fileName,e);
throw CsdRuntimeException.of(ProxyEntityError.UPLOAD_FILE_ERROR);
} finally {
IOUtils.closeQuietly(inputStream);
this.disConnect(connect);
}
}
读取数据
代码语言:javascript复制 public byte[] readFromSftp(String pathname,String filename) throws Exception {
SftpConnect connect = this.getConnect();
ChannelSftp sftp = connect.getSftp();// sftp操作类
byte[] buffer;
InputStream inputStream = null ;
try {
Vector<ChannelSftp.LsEntry> sftpFile = new Vector();
sftp.ls(pathname, entry -> {
if(entry.getFilename().contains(filename)) {
log.info("readFromSftp find match file;fileName={}",filename);
sftpFile.add(entry);
return ChannelSftp.LsEntrySelector.BREAK;
}
return ChannelSftp.LsEntrySelector.CONTINUE;
});
if(CollectionUtil.isEmpty(sftpFile)) {
log.warn("JSchProxy.readFromSftp occur not find target file;pathname={},fileName={}",pathname,filename);
return null;
}
sftp.cd(pathname); //进入目录
inputStream = sftp.get(sftpFile.get(0).getFilename());
buffer = new byte[inputStream.available()];
int read = inputStream.read(buffer);
} catch (Exception e) {
log.error("JSchProxy.upload readFromSftp error;pathname={},fileName={}",pathname,filename,e);
throw CsdRuntimeException.of(ProxyEntityError.UPLOAD_FILE_ERROR);
} finally {
IOUtils.closeQuietly(inputStream);
this.disConnect(connect);
}
return buffer;
}
关闭资源
代码语言:javascript复制 protected void disConnect(SftpConnect connect){
if(null == connect) {
return;
}
ChannelSftp sftp = connect.getSftp();
if(null != sftp){
sftp.disconnect();
sftp.exit();
}
Channel channel = connect.getChannel();
if(null != channel){
channel.disconnect();
}
Session session = connect.getSession();
if(null != session){
session.disconnect();
}
connect = null;//for gc
}
2.Apache FTPSClient方式
建立连接
代码语言:javascript复制 protected FTPSClient initFtpsClient() throws IOException {
FTPSClient ftpsClient = new FTPSClient();
ftpsClient.setControlEncoding("UTF-8");
log.info("connecting...ftps服务器:" this.hostname ":" this.port);
ftpsClient.connect(hostname, port); // 连接ftps服务器
boolean loginRs = ftpsClient.login(username, password); // 登录ftps服务器
log.info("login...ftps服务器;loginRs={},username={},password={}",loginRs,username,password);
ftpsClient.execPBSZ(0);
ftpsClient.execPROT("P");
int replyCode = ftpsClient.getReplyCode(); // 是否成功登录服务器
if (!FTPReply.isPositiveCompletion(replyCode)) {
log.info("connect failed...hostname={},port={},replyCode={}",this.hostname, this.port,replyCode);
}
log.info("connect successful...ftps服务器:" this.hostname ":" this.port);
ftpsClient.setDataTimeout(100000); //设置传输超时时间为60秒
ftpsClient.setConnectTimeout(100000);
return ftpsClient;
}
上传文件
代码语言:javascript复制 public void upload(InputStream inputStream, String pathname, String fileName) {
FTPSClient client = null;
try {
client = this.initFtpsClient();
if(null == client) {
throw CsdRuntimeException.of(ProxyEntityError.CONNECT_FTP_ERROR);
}
client.enterLocalPassiveMode();
client.setFileType(FTP.BINARY_FILE_TYPE);
//String[] rt=client.doCommandAsStrings("pwd","");
//log.info("current work dir is:{}", JSON.toJSONString(rt));
client.changeWorkingDirectory(pathname);
boolean uploadRs = client.storeFile(fileName,inputStream);
log.info("FtpsClientProxy.upload rs={},pathname={},fileName={}",uploadRs,pathname,fileName);
int replyCode = client.getReplyCode();
log.info("FtpsClientProxy.upload replyCode={},pathname={},fileName={}",replyCode,pathname,fileName);
client.logout();
} catch (ConnectException e) {
log.error("FtpsClientProxy.upload occur error;pathname={},fileName={}",pathname,fileName,e);
throw CsdRuntimeException.of(ProxyEntityError.CONNECT_EXCEPTION);
} catch (IOException e) {
log.error("FtpsClientProxy.upload occur error;pathname={},fileName={}",pathname,fileName,e);
throw CsdRuntimeException.of(ProxyEntityError.UPLOAD_FILE_ERROR);
} catch (CsdRuntimeException e) {
log.error("FtpsClientProxy.upload occur error;pathname={},fileName={}",pathname,fileName,e);
throw e;
} catch (Exception e) {
log.error("FtpsClientProxy.upload occur error;pathname={},fileName={}",pathname,fileName,e);
throw CsdRuntimeException.of(ProxyEntityError.SYSTEM_ERROR);
} finally {
IOUtils.closeQuietly(inputStream);
this.disconnect(client);
}
}
读取数据
代码语言:javascript复制 public byte[] readFromSftp(String pathname,FTPFileFilter filter) {
FTPSClient client = null;
byte[] buffer;
InputStream inputStream = null;
try {
client = this.initFtpsClient();
if(null == client) {
throw CsdRuntimeException.of(ProxyEntityError.CONNECT_FTP_ERROR);
}
client.enterLocalPassiveMode();
client.type(FTP.BINARY_FILE_TYPE);
//client.setFileType(FTP.BINARY_FILE_TYPE);
FTPFile[] ftpFiles = client.listFiles(pathname, filter);
//暂且认为只有一个符合条件的文件
if(null == ftpFiles || ftpFiles.length == 0) {
//Alarm.report("Csd find no extensya uploaded file to download...");
throw CsdRuntimeException.of(ProxyEntityError.NO_FILE_FIND);
}
client.changeWorkingDirectory(pathname);
inputStream = client.retrieveFileStream(ftpFiles[0].getName());
buffer = ByteStreams.toByteArray(inputStream);
client.logout();
} catch (IOException e) {
log.error("FtpsClientProxy.readExcel occur error;pathname={}",pathname,e);
throw CsdRuntimeException.of(ProxyEntityError.UPLOAD_FILE_ERROR);
} catch (CsdRuntimeException e) {
log.error("FtpsClientProxy.readExcel occur error;pathname={}",pathname,e);
throw e;
} catch (Exception e) {
log.error("FtpsClientProxy.readExcel occur error;pathname={}",pathname,e);
throw CsdRuntimeException.of(ProxyEntityError.SYSTEM_ERROR);
} finally {
IOUtils.closeQuietly(inputStream);
this.disconnect(client);
}
return buffer;
}
关闭资源
代码语言:javascript复制 protected void disconnect(FTPSClient ftpsClient) {
if(null == ftpsClient) {
log.warn("FtpsClientProxy.disconnect client is null");
return;
}
if(!ftpsClient.isConnected()) {
log.warn("FtpsClientProxy.disconnect client is closed;connected = {}",ftpsClient.isConnected());
return;
}
try {
ftpsClient.disconnect();
} catch (IOException e) {
log.error("FtpsClientProxy.disconnect occur error;",e);
}
}