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

[经验分享] 使用JSch远程部署flume采集点

[复制链接]

尚未签到

发表于 2015-11-27 20:03:35 | 显示全部楼层 |阅读模式
  公司有个需求,为了减少运维人员的工作量,需要开发一个远程部署flume的工具。我这里使用比较方便安全的SFTP协议,前提是需要客户端安装有SSH。Unix、linux、aix系统基本默认安装,windows的要自行安装。
  其实在windows系统中还可以使用SMB文件共享协议来实现文件传输,还有比较通用的FTP协议、Telnet协议,但这里我们选用比较安全,操作方便的SFTP协议,其他的,我在下面也贴出来。
  首先下载依赖jar包:jsch-0.1.52.jar。官网:http://www.jcraft.com/jsch/
  然后编写工具类,具体看代码:
  

package com.coreware.stfp;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Map;
import java.util.Properties;
import java.util.Vector;
import org.apache.log4j.Logger;
import com.jcraft.jsch.Channel;
import com.jcraft.jsch.ChannelExec;
import com.jcraft.jsch.ChannelSftp;
import com.jcraft.jsch.ChannelSftp.LsEntry;
import com.jcraft.jsch.JSch;
import com.jcraft.jsch.JSchException;
import com.jcraft.jsch.Session;
import com.jcraft.jsch.SftpException;
public class SFTPChannel {
private static final Logger LOG = Logger.getLogger(SFTPChannel.class.getName());
private Session session = null;
private ChannelSftp sftp = null;
private ChannelExec exec = null;
public SFTPChannel(String host, int port, String userName, String password, int timeout) {
try {
JSch jsch = new JSch(); // 创建JSch对象
session = jsch.getSession(userName, host, port);// 根据用户名,主机ip,端口获取一个Session对象
LOG.debug("Session created.");
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建立链接
LOG.debug("Session connected.");
} catch (JSchException e) {
e.printStackTrace();
}
}
public SFTPChannel(Map<String, String> map, int timeout) {
try {
String host = map.get(&quot;host&quot;).toString();
int port = Integer.parseInt(map.get(&quot;port&quot;) + &quot;&quot;);
String userName = map.get(&quot;userName&quot;).toString();
String password = map.get(&quot;password&quot;).toString();
JSch jsch = new JSch(); // 创建JSch对象
session = jsch.getSession(userName, host, port);// 根据用户名,主机ip,端口获取一个Session对象
LOG.debug(&quot;Session created.&quot;);
if (password != null) {
session.setPassword(password); // 设置密码
}
Properties config = new Properties();
config.put(&quot;StrictHostKeyChecking&quot;, &quot;no&quot;);
session.setConfig(config); // 为Session对象设置properties
session.setTimeout(timeout); // 设置timeout时间
session.connect(); // 通过Session建立链接
LOG.debug(&quot;Session connected.&quot;);
} catch (JSchException e) {
e.printStackTrace();
}
}
/**
* 打开SFTP通道
*/
public ChannelSftp getSftp() {
LOG.debug(&quot;Opening Channel.&quot;);
try {
Channel channel = session.openChannel(&quot;sftp&quot;);// 打开SFTP通道
channel.connect(); // 建立SFTP通道的连接
sftp = (ChannelSftp) channel;
} catch (JSchException e) {
e.printStackTrace();
}
return sftp;
}
/**
* 打开exec通道
*/
public ChannelExec getExec() {
LOG.debug(&quot;Opening Channel.&quot;);
try {
Channel channel = session.openChannel(&quot;exec&quot;);// 打开SFTP通道
channel.setInputStream(null);
channel.connect(); // 建立SFTP通道的连接
exec = (ChannelExec) channel;
} catch (JSchException e) {
e.printStackTrace();
}
return exec;
}
public void execCmd(String command) throws JSchException {
BufferedReader reader = null;
Channel channel = null;
try {
channel = session.openChannel(&quot;exec&quot;);
((ChannelExec) channel).setCommand(command);
channel.setInputStream(null);
((ChannelExec) channel).setErrStream(System.err);
channel.connect();
reader = new BufferedReader(new InputStreamReader(channel.getInputStream()));
String buf = null;
while ((buf = reader.readLine()) != null) {
System.out.println(buf);
}
} catch (IOException e) {
e.printStackTrace();
} catch (JSchException e) {
e.printStackTrace();
} finally {
try {
reader.close();
} catch (IOException e) {
e.printStackTrace();
}
channel.disconnect();
}
}
public void pwd() throws SftpException {
LOG.info(sftp.pwd());
}
public void ls() throws SftpException {
ls(&quot;.&quot;);
}
public void ls(String path) throws SftpException {
Vector<?> vector = sftp.ls(path);
for (Object object : vector) {
if (object instanceof LsEntry) {
LsEntry entry = LsEntry.class.cast(object);
LOG.info(entry.getFilename());
}
}
}
public void closeChannel() {
if (sftp != null){
sftp.quit();
sftp.disconnect();
}
if (exec != null)
exec.disconnect();
if (session != null) {
session.disconnect();
}
}
public void closeSftp() {
if (sftp != null){
sftp.quit();
sftp.disconnect();
}
}
public void closeExec() {
if (exec != null)
exec.disconnect();
}
}


然后是调用方法:  
  

@RequestMapping(value = &quot;/sftp.do&quot;)
public String uploadBySftp(HttpServletRequest request, HttpServletResponse response) {
String result = &quot;handle failed!&quot;;
SFTPChannel channel = null;
try {
if (logger.isDebugEnabled()) {
logger.debug(&quot;execute uploadBySftp method...&quot;);
}
Map<String, String> params = getParas(request);
String path = params.get(&quot;path&quot;).toString();
String os = params.get(&quot;os&quot;).toString();
String userName = params.get(&quot;userName&quot;).toString();
params.put(&quot;userName&quot;, decoder(userName));
String password = params.get(&quot;password&quot;).toString();
params.put(&quot;password&quot;, decoder(password));
channel = new SFTPChannel(params, 60000);
ChannelSftp chSftp = channel.getSftp();
try {
channel.ls(path);
} catch (Exception e) {
chSftp.mkdir(path);
}
File file = new File(src);
long fileSize = file.length();
chSftp.put(src, path, new FileProgressMonitor(fileSize), ChannelSftp.OVERWRITE);
String fileName = file.getName();
if (file.isFile() && (fileName.endsWith(&quot;.zip&quot;) || fileName.endsWith(&quot;.ZIP&quot;)))
channel.execCmd(&quot;unzip -o &quot; + path + fileName + &quot; -d &quot; + path);
else if (file.isFile() && (fileName.endsWith(&quot;.tar&quot;) || fileName.endsWith(&quot;.tar.gz&quot;)))
channel.execCmd(&quot;tar -zxvf &quot; + path + fileName);
chSftp.rm(path + file.getName());
if (!&quot;windows&quot;.equals(os)) {
channel.execCmd(path + &quot;flume/bin/flume-daemon.sh&quot;);
} else {
channel.execCmd(path + &quot;flume\\bin\\startCWAgen.bat&quot;);
}
result = &quot;success&quot;;
return result;
} catch (SftpException e) {
logger.error(&quot;has a error:{}&quot;, e.getMessage());
e.printStackTrace();
result = &quot;file create failed&quot;;
return result;
} catch (JSchException e) {
logger.error(&quot;has a error:{}&quot;, e.getMessage());
e.printStackTrace();
return result;
} finally {
if (channel != null)
channel.closeChannel();
}
}下面是重写的文件传输的百分比统计:  
  

package com.coreware.stfp;
import java.text.DecimalFormat;
import java.util.Timer;
import java.util.TimerTask;
import com.jcraft.jsch.SftpProgressMonitor;
public class FileProgressMonitor extends TimerTask implements SftpProgressMonitor {
private long progressInterval = 1000; // 默认间隔时间为1秒
private boolean isEnd = false; // 记录传输是否结束
private long transfered; // 记录已传输的数据总大小
private long fileSize; // 记录文件总大小
private Timer timer; // 定时器对象
private boolean isScheduled = false; // 记录是否已启动timer记时器
public FileProgressMonitor(long fileSize) {
this.fileSize = fileSize;
}
@Override
public void run() {
if (!isEnd()) { // 判断传输是否已结束
//System.out.println(&quot;Transfering is in progress.&quot;);
long transfered = getTransfered();
if (transfered != fileSize) { // 判断当前已传输数据大小是否等于文件总大小
//System.out.println(&quot;Current transfered: &quot; + transfered + &quot; bytes&quot;);
sendProgressMessage(transfered);
} else {
System.out.println(&quot;File transfering is done.&quot;);
setEnd(true); // 如果当前已传输数据大小等于文件总大小,说明已完成,设置end
}
} else {
//System.out.println(&quot;Transfering done. Cancel timer.&quot;);
stop(); // 如果传输结束,停止timer记时器
return;
}
}
public void stop() {
//System.out.println(&quot;Try to stop progress monitor.&quot;);
if (timer != null) {
timer.cancel();
timer.purge();
timer = null;
isScheduled = false;
}
System.out.println(&quot;Progress monitor stoped.&quot;);
}
public void start() {
//System.out.println(&quot;Try to start progress monitor.&quot;);
if (timer == null) {
timer = new Timer();
}
timer.schedule(this, 1000, progressInterval);
isScheduled = true;
System.out.println(&quot;Progress monitor started.&quot;);
}
/**
* 打印progress信息
*
* @param transfered
*/
private void sendProgressMessage(long transfered) {
if (fileSize != 0) {
double d = ((double) transfered * 100) / (double) fileSize;
DecimalFormat df = new DecimalFormat(&quot;#.##&quot;);
System.out.println(&quot;Sending progress message: &quot; + df.format(d) + &quot;%&quot;);
} else {
System.out.println(&quot;Sending progress message: &quot; + transfered);
}
}
/**
* 实现了SftpProgressMonitor接口的count方法
*/
public boolean count(long count) {
if (isEnd())
return false;
if (!isScheduled) {
start();
}
add(count);
return true;
}
/**
* 实现了SftpProgressMonitor接口的end方法
*/
public void end() {
setEnd(true);
System.out.println(&quot;transfering end.&quot;);
}
private synchronized void add(long count) {
transfered = transfered + count;
}
private synchronized long getTransfered() {
return transfered;
}
public synchronized void setTransfered(long transfered) {
this.transfered = transfered;
}
private synchronized void setEnd(boolean isEnd) {
this.isEnd = isEnd;
}
private synchronized boolean isEnd() {
return isEnd;
}
public void init(int op, String src, String dest, long max) {
// Not used for putting InputStream
}
}

  
  ------------------------------------------------------------------------------------------------
  以上是SFTP协议的一些方法,下面介绍smb协议操作,依赖的包是jcifs-1.3.18.jar:
  

package com.coreware.smb;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import jcifs.smb.SmbFile;
import jcifs.smb.SmbFileInputStream;
import jcifs.smb.SmbFileOutputStream;
/**
*
* @author T430
*
*/
public class SmbUtil {
/**
* 方法一:
*
* @param remoteUrl
*            远程路径 smb://192.168.75.204/test/新建 文本文档.txt
* @throws IOException
*/
public static void smbGet(String remoteUrl) throws IOException {
SmbFileInputStream in = null;
try {
SmbFile smbFile = new SmbFile(remoteUrl);
int length = smbFile.getContentLength();// 得到文件的大小
byte buffer[] = new byte[length];
in = new SmbFileInputStream(smbFile);
// 建立smb文件输入流
while ((in.read(buffer)) != -1) {
System.out.write(buffer);
System.out.println(buffer.length);
}
} catch (IOException e) {
throw new IOException(e);
} finally {
try {
if (in != null)
in.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
// 从共享目录下载文件
/**
* 方法二: 路径格式:smb://192.168.75.204/test/新建 文本文档.txt
* smb://username:password@192.168.0.77/test
*
* @param remoteUrl
*            远程路径
* @param localDir
*            要写入的本地路径
* @throws Exception
*/
public static void smbGet(String remoteUrl, String localDir) throws IOException {
InputStream in = null;
OutputStream out = null;
try {
SmbFile remoteFile = new SmbFile(remoteUrl);
String fileName = remoteFile.getName();
File localFile = new File(localDir + File.separator + fileName);
in = new BufferedInputStream(new SmbFileInputStream(remoteFile));
out = new BufferedOutputStream(new FileOutputStream(localFile));
byte[] buffer = new byte[1024];
while (in.read(buffer) != -1) {
out.write(buffer);
buffer = new byte[1024];
}
} catch (IOException e) {
throw new IOException(e);
} finally {
try {
if (out != null)
out.close();
if (in != null)
in.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* 向共享目录上传文件 远程url smb://192.168.0.77/test 如果需要用户名密码就这样:
* smb://username:password@192.168.0.77/test
*
* @param remoteUrl
* @param localFilePath
* @throws Exception
*/
public static void smbPut(String remoteUrl, String localFilePath) throws IOException {
InputStream in = null;
OutputStream out = null;
try {
File localFile = new File(localFilePath);
String fileName = localFile.getName();
SmbFile remoteFile = new SmbFile(remoteUrl + &quot;/&quot; + fileName);
in = new BufferedInputStream(new FileInputStream(localFile));
out = new BufferedOutputStream(new SmbFileOutputStream(remoteFile));
byte[] buffer = new byte[1024];
while (in.read(buffer) != -1) {
out.write(buffer);
buffer = new byte[1024];
}
} catch (IOException e) {
throw new IOException(e);
} finally {
try {
if (out != null)
out.close();
if (in != null)
in.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}


FTP的操作,依赖包commons-net-3.3.jar:  
  

package com.coreware.ftp;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Map;
import org.apache.commons.io.IOUtils;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPReply;
public class FtpUtil {
private static FTPClient ftp;
/**
* FTP上传
*
* @param map
* @param src
*/
public static void upload(Map<String, Object> map, String src) {
String host = map.get(&quot;host&quot;).toString();
String userName = map.get(&quot;userName&quot;).toString();
String password = map.get(&quot;password&quot;).toString();
String path = map.get(&quot;path&quot;) == null ? &quot;&quot; : map.get(&quot;path&quot;).toString();
String port = map.get(&quot;port&quot;) == null ? &quot;21&quot; : map.get(&quot;port&quot;).toString();
upload(host, Integer.parseInt(port), userName, password, path, src);
}
/**
* FTP上传
*
* @param host
* @param userName
* @param password
* @param path
* @param src
*/
public static void upload(String host, int port, String userName, String password, String path, String src) {
try {
boolean isConnect = connect(path, host, port, userName, password);
if(isConnect){
File file = new File(src);
upload(file);
}
} catch (IOException e) {
e.printStackTrace();
throw new RuntimeException(&quot;FTP客户端出错!&quot;, e);
} finally {
try {
ftp.disconnect();
} catch (IOException e) {
e.printStackTrace();
throw new RuntimeException(&quot;关闭FTP连接发生异常!&quot;, e);
}
}
}
/**
*
* @param path
*            上传到ftp服务器哪个路径下
* @param addr
*            地址
* @param port
*            端口号
* @param username
*            用户名
* @param password
*            密码
* @return
* @throws Exception
*/
private static boolean connect(String path, String addr, int port, String username, String password) throws IOException {
boolean result = false;
ftp = new FTPClient();
int reply;
ftp.connect(addr, port);
ftp.login(username, password);
ftp.setFileType(FTPClient.BINARY_FILE_TYPE);
reply = ftp.getReplyCode();
if (!FTPReply.isPositiveCompletion(reply)) {
ftp.disconnect();
return result;
}
ftp.changeWorkingDirectory(path);
ftp.setBufferSize(1024);
ftp.setControlEncoding(&quot;GBK&quot;);
result = true;
return result;
}
/**
*
* @param file
*            上传的文件或文件夹
* @throws Exception
*/
private static void upload(File file) throws IOException {
if (file.isDirectory()) {
ftp.makeDirectory(file.getName());
ftp.changeWorkingDirectory(file.getName());
String[] files = file.list();
for (int i = 0; i < files.length; i++) {
File file1 = new File(file.getPath() + &quot;\\&quot; + files);
if (file1.isDirectory()) {
upload(file1);
ftp.changeToParentDirectory();
} else {
File file2 = new File(file.getPath() + &quot;\\&quot; + files);
FileInputStream input = new FileInputStream(file2);
ftp.storeFile(file2.getName(), input);
input.close();
}
}
} else {
File file2 = new File(file.getPath());
FileInputStream input = new FileInputStream(file2);
ftp.storeFile(file2.getName(), input);
input.close();
}
}
/**
* FTP下载
*
* @param host
* @param userName
* @param password
* @param path
* @param src
*/
public static void download(String host, String userName, String password, String path, String src) {
FTPClient ftpClient = new FTPClient();
FileOutputStream fos = null;
try {
ftpClient.connect(host);
ftpClient.login(userName, password);
String remoteFileName = path;
fos = new FileOutputStream(src);
ftpClient.setBufferSize(1024);
// 设置文件类型(二进制)
ftpClient.setFileType(FTPClient.BINARY_FILE_TYPE);
ftpClient.retrieveFile(remoteFileName, fos);
} catch (IOException e) {
e.printStackTrace();
throw new RuntimeException(&quot;FTP客户端出错!&quot;, e);
} finally {
IOUtils.closeQuietly(fos);
try {
ftpClient.disconnect();
} catch (IOException e) {
e.printStackTrace();
throw new RuntimeException(&quot;关闭FTP连接发生异常!&quot;, e);
}
}
}
}
Telnet协议,依赖包commons-net-3.3.jar:


  

package com.coreware.telnet;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.net.SocketException;
import org.apache.commons.net.telnet.TelnetClient;
public class TelnetUtil {
private static InputStream in;
private static PrintStream out;
private static String prompt = &quot;#&quot;;
private static String os = &quot;windows&quot;;
public static void handle(String host, int port, String os, String user, String pwd) {
TelnetClient telnet = null;
try {
if (&quot;windows&quot;.equals(os)) {
telnet = new TelnetClient(&quot;VT220&quot;);
prompt = &quot;&quot;;
} else {
TelnetUtil.os = os;
telnet = new TelnetClient();
prompt = user.equals(&quot;root&quot;) ? &quot;#&quot; : &quot;$&quot;;
}
telnet.connect(host, port);
telnet.setSoTimeout(60000);
in = telnet.getInputStream();
out = new PrintStream(telnet.getOutputStream());
login(user, pwd);
//sendCommand(&quot;cd /d \&quot;D:/ftp/flume/bin\&quot;&quot;);
//sendCommand(&quot;start \&quot;\&quot; \&quot;D:/ftp/flume/bin/startCWAgen.bat\&quot;&quot;);
sendCommand(&quot;java -jar \&quot;D:/ftp/flume/lib/controller.jar\&quot; \&quot;D:/ftp/flume/lib/config.properties\&quot;&quot;);
readUntil();
} catch (SocketException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (telnet != null)
telnet.disconnect();
} catch (IOException e) {
e.printStackTrace();
try {
throw new Exception(&quot;telnet 关闭失败&quot;);
} catch (Exception e1) {
e1.printStackTrace();
}
}
}
}
public static void handle(String host, String os, String user, String pwd) {
int port = 23;
handle(host, port, os, user, pwd);
}
private static void login(String user, String password) {
readUntil(&quot;login:&quot;);
write(user);
if (&quot;windows&quot;.equals(os)) {
readUntil(&quot;password:&quot;);
} else {
readUntil(&quot;Password:&quot;);
}
write(password);
readUntil(prompt + &quot; &quot;);
}
private static void su(String password) {
try {
write(&quot;su&quot;);
readUntil(&quot;Password: &quot;);
write(password);
prompt = &quot;#&quot;;
readUntil(prompt + &quot; &quot;);
} catch (Exception e) {
e.printStackTrace();
}
}
private static void write(String value) {
try {
out.println(value);
out.flush();
System.out.println(value);
} catch (Exception e) {
e.printStackTrace();
}
}
public static String sendCommand(String command) {
try {
write(command);
return readUntil(prompt + &quot; &quot;);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
private static String readUntil() {
InputStreamReader isr = null;
BufferedReader br = null;
try {
//StringBuffer sb = new StringBuffer();
isr = new InputStreamReader(in, &quot;GBK&quot;);
br = new BufferedReader(isr);
int str = 0;
while ((str = br.read()) != -1) {
//sb.append((char) str);
System.out.print((char) str);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (isr != null)
isr.close();
if (br != null)
br.close();
} catch (Exception e) {
e.printStackTrace();
}
}
return null;
}
private static String readUntil(String pattern) {
StringBuffer sb = new StringBuffer();
try {
char lastChar = pattern.charAt(pattern.length() - 1);
char ch = (char) in.read();
while (true) {
sb.append(ch);
if (ch == lastChar) {
if (sb.toString().endsWith(pattern)) {
byte[] temp = sb.toString().getBytes(&quot;iso8859-1&quot;);// 处理编码,界面显示乱码问题
return new String(temp, &quot;GBK&quot;);
}
}
ch = (char) in.read();
}
} catch (Exception e) {
e.printStackTrace();
}
return sb.toString();
}
public static void main(String[] args) {
TelnetUtil.handle(&quot;127.0.0.1&quot;, &quot;windows&quot;, &quot;bob&quot;, &quot;123&quot;);
}
}

  

运维网声明 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-144358-1-1.html 上篇帖子: log4j与flume整合配置 下篇帖子: flume source、sink、Channels测试
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

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

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

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

扫描微信二维码查看详情

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


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


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


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



合作伙伴: 青云cloud

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