基于JSch的Sftp工具类
本Sftp工具类的API如下所示。
1)构造方法摘要
|
参数:
|
2)方法摘要
boolean |
changeDir(String pathName) 切换工作目录 |
| boolean | changeToHomeDir() 切换到根目录 |
| boolean | changeToParentDir() 切换到上一级目录 |
| String | currentDir() 当前工作目录 |
| boolean | delDir(String dirName) 删除文件夹 |
| boolean | delFile(String fileName) 删除文件 |
| boolean | downloadFile(String remotePath, String fileName,String localPath) 下载文件 |
| boolean | exist(String name) 当前目录是否存在文件或文件夹 |
| boolean | exist(String path, String name) 指定目录下,是否存在文件或文件夹 |
| boolean | existDir(String name) 当前目录是否存在文件夹 |
| boolean | existDir(String path, String name) 指定目录下,是否存在文件夹 |
| boolean | existFile(String name) 当前目录是否存在文件 |
| boolean | existFile(String path, String name) 指定目录下,是否存在文件 |
| boolean | login() 登陆SFTP服务器 |
| void | logout() 登出 |
| String[] | ls() 当前目录下文件及文件夹名称列表 |
| String[] | ls(String pathName) 指定目录下文件及文件夹名称列表 |
| String[] | lsDirs() 当前目录下文件夹名称列表 |
| String[] | lsDirs(String pathName) 指定目录下文件夹名称列表 |
| String[] | lsFiles() 当前目录下文件名称列表 |
| String[] | lsFiles(String pathName) 指定目录下文件名称列表 |
boolean |
makeDir(String dirName) 创建目录 |
| boolean | uploadFile(String pathName,String fileName,InputStream input) 上传文件 |
| boolean | uploadFile(String pathName, String fileName, String localFile) 上传文件 |
3)源代码
import java.io.File;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import java.util.Vector;
import org.apache.log4j.Logger;
import com.jcraft.jsch.ChannelSftp;
import com.jcraft.jsch.JSch;
import com.jcraft.jsch.JSchException;
import com.jcraft.jsch.Session;
import com.jcraft.jsch.SftpException;
import com.jcraft.jsch.ChannelSftp.LsEntry; /**
* SFTP(Secure File Transfer Protocol),安全文件传送协议。
* @version 1.0 2014/12/18
* @author dongliyang
*/
public class Sftp { /** 日志记录器 */
private Logger logger = Logger.getLogger(Sftp.class);
/** Session */
private Session session = null;
/** Channel */
private ChannelSftp channel = null;
/** SFTP服务器IP地址 */
private String host;
/** SFTP服务器端口 */
private int port;
/** 连接超时时间,单位毫秒 */
private int timeout; /** 用户名 */
private String username;
/** 密码 */
private String password; /**
* SFTP 安全文件传送协议
* @param host SFTP服务器IP地址
* @param port SFTP服务器端口
* @param timeout 连接超时时间,单位毫秒
* @param username 用户名
* @param password 密码
*/
public Sftp(String host,int port,int timeout,String username,String password){
this.host = host;
this.port = port;
this.timeout = timeout;
this.username = username;
this.password = password;
} /**
* 登陆SFTP服务器
* @return boolean
*/
public boolean login() { try {
JSch jsch = new JSch();
session = jsch.getSession(username, host, port);
if(password != null){
session.setPassword(password);
}
Properties config = new Properties();
config.put("StrictHostKeyChecking", "no");
session.setConfig(config);
session.setTimeout(timeout);
session.connect();
logger.debug("sftp session connected"); logger.debug("opening channel");
channel = (ChannelSftp)session.openChannel("sftp");
channel.connect(); logger.debug("connected successfully");
return true;
} catch (JSchException e) {
logger.error("sftp login failed",e);
return false;
}
} /**
* 上传文件
* <p>
* 使用示例,SFTP服务器上的目录结构如下:/testA/testA_B/
* <table border="1">
* <tr><td>当前目录</td><td>方法</td><td>参数:绝对路径/相对路径</td><td>上传后</td></tr>
* <tr><td>/</td><td>uploadFile("testA","upload.txt",new FileInputStream(new File("up.txt")))</td><td>相对路径</td><td>/testA/upload.txt</td></tr>
* <tr><td>/</td><td>uploadFile("testA/testA_B","upload.txt",new FileInputStream(new File("up.txt")))</td><td>相对路径</td><td>/testA/testA_B/upload.txt</td></tr>
* <tr><td>/</td><td>uploadFile("/testA/testA_B","upload.txt",new FileInputStream(new File("up.txt")))</td><td>绝对路径</td><td>/testA/testA_B/upload.txt</td></tr>
* </table>
* </p>
* @param pathName SFTP服务器目录
* @param fileName 服务器上保存的文件名
* @param input 输入文件流
* @return boolean
*/
public boolean uploadFile(String pathName,String fileName,InputStream input){ String currentDir = currentDir();
if(!changeDir(pathName)){
return false;
} try {
channel.put(input,fileName,ChannelSftp.OVERWRITE);
if(!existFile(fileName)){
logger.debug("upload failed");
return false;
}
logger.debug("upload successful");
return true;
} catch (SftpException e) {
logger.error("upload failed",e);
return false;
} finally {
changeDir(currentDir);
}
} /**
* 上传文件
* <p>
* 使用示例,SFTP服务器上的目录结构如下:/testA/testA_B/
* <table border="1">
* <tr><td>当前目录</td><td>方法</td><td>参数:绝对路径/相对路径</td><td>上传后</td></tr>
* <tr><td>/</td><td>uploadFile("testA","upload.txt","up.txt")</td><td>相对路径</td><td>/testA/upload.txt</td></tr>
* <tr><td>/</td><td>uploadFile("testA/testA_B","upload.txt","up.txt")</td><td>相对路径</td><td>/testA/testA_B/upload.txt</td></tr>
* <tr><td>/</td><td>uploadFile("/testA/testA_B","upload.txt","up.txt")</td><td>绝对路径</td><td>/testA/testA_B/upload.txt</td></tr>
* </table>
* </p>
* @param pathName SFTP服务器目录
* @param fileName 服务器上保存的文件名
* @param localFile 本地文件
* @return boolean
*/
public boolean uploadFile(String pathName,String fileName,String localFile){ String currentDir = currentDir();
if(!changeDir(pathName)){
return false;
} try {
channel.put(localFile,fileName,ChannelSftp.OVERWRITE);
if(!existFile(fileName)){
logger.debug("upload failed");
return false;
}
logger.debug("upload successful");
return true;
} catch (SftpException e) {
logger.error("upload failed",e);
return false;
} finally {
changeDir(currentDir);
}
} /**
* 下载文件
* <p>
* 使用示例,SFTP服务器上的目录结构如下:/testA/testA_B/
* <table border="1">
* <tr><td>当前目录</td><td>方法</td><td>参数:绝对路径/相对路径</td><td>下载后</td></tr>
* <tr><td>/</td><td>downloadFile("testA","down.txt","D:\\downDir")</td><td>相对路径</td><td>D:\\downDir\\down.txt</td></tr>
* <tr><td>/</td><td>downloadFile("testA/testA_B","down.txt","D:\\downDir")</td><td>相对路径</td><td>D:\\downDir\\down.txt</td></tr>
* <tr><td>/</td><td>downloadFile("/testA/testA_B","down.txt","D:\\downDir")</td><td>绝对路径</td><td>D:\\downDir\\down.txt</td></tr>
* </table>
* </p>
* @param remotePath SFTP服务器目录
* @param fileName 服务器上需要下载的文件名
* @param localPath 本地保存路径
* @return boolean
*/
public boolean downloadFile(String remotePath,String fileName,String localPath){ String currentDir = currentDir();
if(!changeDir(remotePath)){
return false;
} try {
String localFilePath = localPath + File.separator + fileName;
channel.get(fileName,localFilePath); File localFile = new File(localFilePath);
if(!localFile.exists()){
logger.debug("download file failed");
return false;
}
logger.debug("download successful");
return true;
} catch (SftpException e) {
logger.error("download file failed",e);
return false;
} finally {
changeDir(currentDir);
}
} /**
* 切换工作目录
* <p>
* 使用示例,SFTP服务器上的目录结构如下:/testA/testA_B/
* <table border="1">
* <tr><td>当前目录</td><td>方法</td><td>参数(绝对路径/相对路径)</td><td>切换后的目录</td></tr>
* <tr><td>/</td><td>changeDir("testA")</td><td>相对路径</td><td>/testA/</td></tr>
* <tr><td>/</td><td>changeDir("testA/testA_B")</td><td>相对路径</td><td>/testA/testA_B/</td></tr>
* <tr><td>/</td><td>changeDir("/testA")</td><td>绝对路径</td><td>/testA/</td></tr>
* <tr><td>/testA/testA_B/</td><td>changeDir("/testA")</td><td>绝对路径</td><td>/testA/</td></tr>
* </table>
* </p>
* @param pathName 路径
* @return boolean
*/
public boolean changeDir(String pathName){
if(pathName == null || pathName.trim().equals("")){
logger.debug("invalid pathName");
return false;
} try {
channel.cd(pathName.replaceAll("\\\\", "/"));
logger.debug("directory successfully changed,current dir=" + channel.pwd());
return true;
} catch (SftpException e) {
logger.error("failed to change directory",e);
return false;
}
} /**
* 切换到上一级目录
* <p>
* 使用示例,SFTP服务器上的目录结构如下:/testA/testA_B/
* <table border="1">
* <tr><td>当前目录</td><td>方法</td><td>切换后的目录</td></tr>
* <tr><td>/testA/</td><td>changeToParentDir()</td><td>/</td></tr>
* <tr><td>/testA/testA_B/</td><td>changeToParentDir()</td><td>/testA/</td></tr>
* </table>
* </p>
* @return boolean
*/
public boolean changeToParentDir(){
return changeDir("..");
} /**
* 切换到根目录
* @return boolean
*/
public boolean changeToHomeDir(){
String homeDir = null;
try {
homeDir = channel.getHome();
} catch (SftpException e) {
logger.error("can not get home directory",e);
return false;
}
return changeDir(homeDir);
} /**
* 创建目录
* <p>
* 使用示例,SFTP服务器上的目录结构如下:/testA/testA_B/
* <table border="1">
* <tr><td>当前目录</td><td>方法</td><td>参数(绝对路径/相对路径)</td><td>创建成功后的目录</td></tr>
* <tr><td>/testA/testA_B/</td><td>makeDir("testA_B_C")</td><td>相对路径</td><td>/testA/testA_B/testA_B_C/</td></tr>
* <tr><td>/</td><td>makeDir("/testA/testA_B/testA_B_D")</td><td>绝对路径</td><td>/testA/testA_B/testA_B_D/</td></tr>
* </table>
* <br/>
* <b>注意</b>,当<b>中间目录不存在</b>的情况下,不能够使用绝对路径的方式期望创建中间目录及目标目录。
* 例如makeDir("/testNOEXIST1/testNOEXIST2/testNOEXIST3"),这是错误的。
* </p>
* @param dirName 目录
* @return boolean
*/
public boolean makeDir(String dirName){
try {
channel.mkdir(dirName);
logger.debug("directory successfully created,dir=" + dirName);
return true;
} catch (SftpException e) {
logger.error("failed to create directory", e);
return false;
}
} /**
* 删除文件夹
* @param dirName
* @return boolean
*/
@SuppressWarnings("unchecked")
public boolean delDir(String dirName){
if(!changeDir(dirName)){
return false;
} Vector<LsEntry> list = null;
try {
list = channel.ls(channel.pwd());
} catch (SftpException e) {
logger.error("can not list directory",e);
return false;
} for(LsEntry entry : list){
String fileName = entry.getFilename();
if(!fileName.equals(".") && !fileName.equals("..")){
if(entry.getAttrs().isDir()){
delDir(fileName);
} else {
delFile(fileName);
}
}
} if(!changeToParentDir()){
return false;
} try {
channel.rmdir(dirName);
logger.debug("directory " + dirName + " successfully deleted");
return true;
} catch (SftpException e) {
logger.error("failed to delete directory " + dirName,e);
return false;
}
} /**
* 删除文件
* @param fileName 文件名
* @return boolean
*/
public boolean delFile(String fileName){
if(fileName == null || fileName.trim().equals("")){
logger.debug("invalid filename");
return false;
} try {
channel.rm(fileName);
logger.debug("file " + fileName + " successfully deleted");
return true;
} catch (SftpException e) {
logger.error("failed to delete file " + fileName,e);
return false;
}
} /**
* 当前目录下文件及文件夹名称列表
* @return String[]
*/
public String[] ls(){
return list(Filter.ALL);
} /**
* 指定目录下文件及文件夹名称列表
* @return String[]
*/
public String[] ls(String pathName){
String currentDir = currentDir();
if(!changeDir(pathName)){
return new String[0];
};
String[] result = list(Filter.ALL);
if(!changeDir(currentDir)){
return new String[0];
}
return result;
} /**
* 当前目录下文件名称列表
* @return String[]
*/
public String[] lsFiles(){
return list(Filter.FILE);
} /**
* 指定目录下文件名称列表
* @return String[]
*/
public String[] lsFiles(String pathName){
String currentDir = currentDir();
if(!changeDir(pathName)){
return new String[0];
};
String[] result = list(Filter.FILE);
if(!changeDir(currentDir)){
return new String[0];
}
return result;
} /**
* 当前目录下文件夹名称列表
* @return String[]
*/
public String[] lsDirs(){
return list(Filter.DIR);
} /**
* 指定目录下文件夹名称列表
* @return String[]
*/
public String[] lsDirs(String pathName){
String currentDir = currentDir();
if(!changeDir(pathName)){
return new String[0];
};
String[] result = list(Filter.DIR);
if(!changeDir(currentDir)){
return new String[0];
}
return result;
} /**
* 当前目录是否存在文件或文件夹
* @param name 名称
* @return boolean
*/
public boolean exist(String name){
return exist(ls(), name);
} /**
* 指定目录下,是否存在文件或文件夹
* @param path 目录
* @param name 名称
* @return boolean
*/
public boolean exist(String path,String name){
return exist(ls(path),name);
} /**
* 当前目录是否存在文件
* @param name 文件名
* @return boolean
*/
public boolean existFile(String name){
return exist(lsFiles(),name);
} /**
* 指定目录下,是否存在文件
* @param path 目录
* @param name 文件名
* @return boolean
*/
public boolean existFile(String path,String name){
return exist(lsFiles(path), name);
} /**
* 当前目录是否存在文件夹
* @param name 文件夹名称
* @return boolean
*/
public boolean existDir(String name){
return exist(lsDirs(), name);
} /**
* 指定目录下,是否存在文件夹
* @param path 目录
* @param name 文家夹名称
* @return boolean
*/
public boolean existDir(String path,String name){
return exist(lsDirs(path), name);
} /**
* 当前工作目录
* @return String
*/
public String currentDir(){
try {
return channel.pwd();
} catch (SftpException e) {
logger.error("failed to get current dir",e);
return homeDir();
}
} /**
* 登出
*/
public void logout(){
if(channel != null){
channel.quit();
channel.disconnect();
}
if(session != null){
session.disconnect();
}
logger.debug("logout successfully");
} //------private method ------ /** 枚举,用于过滤文件和文件夹 */
private enum Filter {/** 文件及文件夹 */ ALL ,/** 文件 */ FILE ,/** 文件夹 */ DIR }; /**
* 列出当前目录下的文件及文件夹
* @param filter 过滤参数
* @return String[]
*/
@SuppressWarnings("unchecked")
private String[] list(Filter filter){
Vector<LsEntry> list = null;
try {
//ls方法会返回两个特殊的目录,当前目录(.)和父目录(..)
list = channel.ls(channel.pwd());
} catch (SftpException e) {
logger.error("can not list directory",e);
return new String[0];
} List<String> resultList = new ArrayList<String>();
for(LsEntry entry : list){
if(filter(entry, filter)){
resultList.add(entry.getFilename());
}
}
return resultList.toArray(new String[0]);
} /**
* 判断是否是否过滤条件
* @param entry LsEntry
* @param f 过滤参数
* @return boolean
*/
private boolean filter(LsEntry entry,Filter f){
if(f.equals(Filter.ALL)){
return !entry.getFilename().equals(".") && !entry.getFilename().equals("..");
} else if(f.equals(Filter.FILE)){
return !entry.getFilename().equals(".") && !entry.getFilename().equals("..") && !entry.getAttrs().isDir();
} else if(f.equals(Filter.DIR)){
return !entry.getFilename().equals(".") && !entry.getFilename().equals("..") && entry.getAttrs().isDir();
}
return false;
} /**
* 根目录
* @return String
*/
private String homeDir(){
try {
return channel.getHome();
} catch (SftpException e) {
return "/";
}
} /**
* 判断字符串是否存在于数组中
* @param strArr 字符串数组
* @param str 字符串
* @return boolean
*/
private boolean exist(String[] strArr,String str){
if(strArr == null || strArr.length == 0){
return false;
}
if(str == null || str.trim().equals("")){
return false;
}
for(String s : strArr){
if(s.equalsIgnoreCase(str)){
return true;
}
}
return false;
} //------private method ------
}
4)测试类
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.util.Arrays; public class SftpTest {
public static void main(String[] args) {
// testLogin();
// testMakeDir();
// testDelFile();
// testDelEmptyDir();
// testDir();
// testLs();
// testParamLs();
// testChangeDir();
// testExist();
// testParamExist();
// testUploadFile();
// testUploadFile2();
// testDownload();
} public static void testLogin(){ //OK
Sftp sftp = getSftp(); sftp.login();
sftp.logout();
} public static void testMakeDir(){ //OK
Sftp sftp = getSftp();
sftp.login();
sftp.makeDir("test2");
sftp.changeDir("test2");
sftp.makeDir("/test2/test2_1");
sftp.logout();
} public static void testDelFile(){ //OK
Sftp sftp = getSftp();
sftp.login();
sftp.delFile("file1.txt");
sftp.logout();
} public static void testDelEmptyDir(){ //OK
Sftp sftp = getSftp();
sftp.login();
sftp.delDir("test3");
sftp.logout();
} public static void testDir(){ //OK
Sftp sftp = getSftp();
sftp.login();
sftp.delDir("test4");
sftp.logout();
} public static void testLs(){ //OK
Sftp sftp = getSftp();
sftp.login();
System.out.println(Arrays.toString(sftp.ls()));
System.out.println(Arrays.toString(sftp.lsFiles()));
System.out.println(Arrays.toString(sftp.lsDirs()));
sftp.logout();
} public static void testParamLs(){ //OK
Sftp sftp = getSftp();
sftp.login();
System.out.println(Arrays.toString(sftp.ls("test1/test4")));
System.out.println(Arrays.toString(sftp.lsFiles("test1/test4")));
System.out.println(Arrays.toString(sftp.lsDirs("test1/test4")));
sftp.logout();
} public static void testChangeDir(){ //OK
Sftp sftp = getSftp();
sftp.login();
sftp.changeDir("test1");
sftp.changeDir("/test1/test4");
sftp.changeToParentDir();
sftp.changeToHomeDir();
sftp.logout();
} public static void testExist(){ //OK
Sftp sftp = getSftp();
sftp.login();
System.out.println(sftp.exist("2fs.docx"));
System.out.println(sftp.exist("test1"));
System.out.println(sftp.existDir("test2"));
System.out.println(sftp.existDir("2sfs.txt"));
System.out.println(sftp.existFile("2sfs.txt"));
System.out.println(sftp.existFile("test2"));
sftp.logout();
} public static void testParamExist(){ //OK
Sftp sftp = getSftp();
sftp.login();
System.out.println(sftp.exist("test1","test4"));
System.out.println(sftp.exist("test1","test_bak.jpg"));
System.out.println(sftp.existDir("/test1","test3"));
System.out.println(sftp.existDir("/test1","test_bak.jpg"));
System.out.println(sftp.existFile("test1","test_bak.jpg"));
System.out.println(sftp.existFile("test1","test2"));
sftp.logout();
} public static void testUploadFile(){ //OK
Sftp sftp = getSftp();
sftp.login();
sftp.uploadFile("/test1/test3", "test_bak2.jpg", "D:\\test.jpg");
try {
sftp.uploadFile("/test1/test2", "test_bak3.jpg", new FileInputStream("D:\\test.jpg"));
} catch (FileNotFoundException e) {
e.printStackTrace();
}
sftp.logout();
} public static void testUploadFile2(){ //OK
Sftp sftp = getSftp();
sftp.login();
sftp.uploadFile("test1/test3", "test_bak2.jpg", "D:\\test.jpg");
try {
sftp.uploadFile("test1/test2", "test_bak3.jpg", new FileInputStream("D:\\test.jpg"));
} catch (FileNotFoundException e) {
e.printStackTrace();
}
sftp.logout();
} public static void testDownload(){ //OK
Sftp sftp = getSftp();
sftp.login();
sftp.downloadFile("test1", "test_bak.jpg", "D:\\testdown");
sftp.downloadFile("/", "2fs.docx", "D:\\testdown");
sftp.logout();
} private static Sftp getSftp(){ String host = "192.168.10.252";
int port = 22;
int timeout = 60000;
String username = "dongliyang";
String password = "dongliyang"; Sftp sftp = new Sftp(host, port, timeout, username, password); return sftp;
}
}
基于JSch的Sftp工具类的更多相关文章
- SFTP工具类
1.SFTP搭建方法: 地址: http://www.jb51.net/article/101405.htm https://blog.csdn.net/helloloser/article/deta ...
- SFTP工具类 操作服务器
package com.leadbank.oprPlatform.util;import com.jcraft.jsch.*;import com.jcraft.jsch.ChannelSftp.Ls ...
- iOS开发——基于corelocation位置定位——工具类
(代码工具类已写好,空闲时间整理成文档,待更新……)
- 基于SqlClient开发SQLServer工具类 伸手党的福音
注意:代码直接Copy调用即可,SQLServer工具类跟我上一个Oracle工具类有所不同,区别在于调用存储过程中时参数的使用,无需输入对应的存储游标名称 特点:根据用户传入的参数类型来识别存储中对 ...
- FTP+SFTP工具类封装-springmore让开发更简单
github地址:https://github.com/tangyanbo/springmore FTPUtil 该工具基于org.apache.commons.net.ftp.FTPClient进行 ...
- jsch连接Linux工具类
import com.alibaba.fastjson.JSONObject;import com.jcraft.jsch.*;import org.slf4j.Logger;import org.s ...
- java SFTP工具类
需要导入jsch-0.1.52.jar import java.io.File; import java.io.FileInputStream; import java.io.FileOutputSt ...
- Java 基于log4j的日志工具类
对log4j日志类进行了简单封装,使用该封装类的优势在于以下两点: 1.不必在每个类中去创建对象,直接类名 + 方法即可 2.可以很方便的打印出堆栈信息 package com.tradeplatfo ...
- 基于durid的JDBCUtils工具类
1.JDBCUtils类 package com.alphajuns.utils; import com.alibaba.druid.pool.DruidDataSourceFactory; impo ...
随机推荐
- 用于主题检测的临时日志(9ef9ede0-46b6-438d-8fb4-ba6b911a34fa - 3bfe001a-32de-4114-a6b4-4005b770f6d7)
这是一个未删除的临时日志.请手动删除它.(258a3e7d-420b-4c9f-bba2-e0ee0f0f198b - 3bfe001a-32de-4114-a6b4-4005b770f6d7)
- Safari 快捷键
标签和网页导航快捷键 8 个 切换到下一个标签页 – Control+Tab 切换到上一个标签页 – Control+Shift+Tab 向下滚动一屏 – 空格 向上滚动一屏 – Shift+空格 焦 ...
- spring annotation简述
一.Annotation基本概念 Annotation是jdk5以后出现的新特性,在jdk中,其内置了许多自己的Annotation,例如@Override,@SuppresWarning,@Depr ...
- Sql Server 2008R2 遇到了BCP导入各种中文乱码的问题
今天玩BCP导入数据的时候,有文件格式,有中文字符串问题……以下是历程,和大家分享一下,希望不要走我的弯路 主要那个表是一个翻译表,一个文件里面内涵几十种语言,所以很容易发现问题. 0.使用最常用的语 ...
- Linux 性能优化工具 perf top
1. perf perf 是一个调查 Linux 中各种性能问题的有力工具. NAME perf - Performance analysis tools for Linux SYNOPSIS per ...
- Windows 保存BMP图片
在Windows下保存BMP图片还是挺方便的,直接上代码,拷贝就能用 void savebmp(uchar * pdata, char * bmp_file, int width, int heigh ...
- 烂泥:nginx负载均衡
本文由秀依林枫提供友情赞助,首发于烂泥行天下. 今天我们来学习下有关nginx的负载均衡配置.nginx的负载均衡是通过nginx的upstream模块和proxy_pass反向代理来实现的. 说明: ...
- Android界面隐藏软键盘的探索(兼findViewById返回null解决办法)
最近写的APP,老师说我的登陆界面虽然有ScrollView滑动,但用户体验不太好,因为软键盘会挡住输入框或登录button(小米Pad,横屏,当指定只能输入数字时没找到关闭系统自带键盘的下箭头). ...
- ganglia安装简记
首先需要安装EPEL的源. yum install -y ganglia.x86_64 ganglia-gmetad.x86_64 ganglia-web.x86_64 ganglia-gmond.x ...
- [转]How to open specific page in the application by clicking on the notification
本文转自:https://github.com/phonegap-build/PushPlugin/issues/213 问:Hello, I would like to know how can I ...