使用 onClick 从远程服务器下载 SFTP
SFTP download from a remote server with onClick
我目前正在开发一个应用程序,该应用程序可以打印来自远程服务器目录的文件列表,并能够单击指定文件并下载它。
到目前为止,这是我所拥有的:
public class MainActivity extends Activity {
private String user = "username";
private String pass = "password";
private String host = "hostname";
private int portNum = 22;
private static final String SFTPWORKINGDIR = "/path/to/file";
private String fileName;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final ListView list = (ListView)findViewById(R.id.choose_sound_listView);
new AsyncTask<Void, Void, List<String>>() {
@Override
protected List<String> doInBackground(Void... params) {
try {
return executeRemoteCommand(user, pass, host, portNum);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
@Override
public void onPostExecute(List<String> soundNames){
ArrayAdapter<String> adapter = new ArrayAdapter<String>(MainActivity.this,android.R.layout.simple_expandable_list_item_1, android.R.id.text1, soundNames);
list.setAdapter(adapter);
list.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> arg0, View arg1,
int position, long arg3) {
String newFileName = (String) list.getItemAtPosition(position);
fileName = newFileName;
Downloader(fileName);
// Show Alert
Toast.makeText(getApplicationContext(),
"Downloaded " + fileName , Toast.LENGTH_LONG).show();
}
});
}
}.execute();
}
public List<String> executeRemoteCommand(String username, String password, String hostname, int port) throws Exception {
List<String> soundNames = new ArrayList<String>();
JSch jsch = new JSch();
Session session = jsch.getSession(username, hostname, port);
session.setPassword(password);
// Avoid asking for key confirmation
Properties prop = new Properties();
prop.put("StrictHostKeyChecking", "no");
session.setConfig(prop);
session.connect();
String command = "ls -la | awk '{ print }'";
Channel channel = session.openChannel("exec");
((ChannelExec) channel).setCommand(command);
((ChannelExec) channel).setErrStream(System.err);
InputStream in = channel.getInputStream();
System.out.println("Connect to session...");
channel.connect();
StringBuffer buffer = new StringBuffer();
//This is the recommended way to read the output
byte[] tmp = new byte[1024];
while (true) {
while (in.available() > 0) {
int i = in.read(tmp, 0, 1024);
if (i < 0) {
break;
}
buffer.append(new String(tmp, 0, i));
}
if (channel.isClosed()) {
System.out.println("exit-status: " + channel.getExitStatus());
break;
}
try {
Thread.sleep(1000);
} catch (Exception ee) {
}
}
channel.disconnect();
session.disconnect();
Scanner scanner = new Scanner(buffer.toString());
while (scanner.hasNextLine()){
String line = scanner.nextLine();
soundNames.add(line);
Log.i("SSH", ": " + line);
}
return soundNames;
}
public void Downloader(String fileName) {
JSch jsch = new JSch();
Session session = null;
Channel channel = null;
ChannelSftp sftpChannel = null;
try {
session = jsch.getSession(user, host, portNum);
session.setConfig("StrictHostKeyChecking", "no");
session.setPassword(pass);
session.connect();
channel = session.openChannel("sftp");
channel.connect();
sftpChannel = (ChannelSftp) channel;
sftpChannel.cd(SFTPWORKINGDIR); //cd to dir that contains file
try {
byte[] buffer = new byte[1024];
BufferedInputStream bis = new BufferedInputStream(sftpChannel.get(fileName));
File path = Environment.getExternalStoragePublicDirectory(
Environment.DIRECTORY_DOCUMENTS);
File newFile = new File(path, "/" + fileName);
Log.d("Dir" , " " + newFile);
OutputStream os = new FileOutputStream(newFile);
BufferedOutputStream bos = new BufferedOutputStream(os);
int readCount;
while( (readCount = bis.read(buffer)) > 0) {
Log.d("Downloading", " " + fileName );
bos.write(buffer, 0, readCount);
}
bis.close();
bos.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
Log.d( "SUCCESS ", fileName + " has been downloaded!!!!");
sftpChannel.exit();
session.disconnect();
} catch (JSchException e) {
e.printStackTrace();
} catch (SftpException e) {
e.printStackTrace();
}
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.menu_main, menu);
return true;
}
}
但是,当我在 listView 中单击该文件时,出现跟踪错误:
com.jcraft.jsch.JSchException: android.os.NetworkOnMainThreadException
编辑: 运行 它在后台使用 AsyncTask 似乎修复了错误,但如果我这样做,应用程序将无法下载指定的file onClick,除非我硬编码文件的名称。
有人知道我该如何解决这个问题吗?
您正在主应用程序线程上执行网络 I/O,如异常所示。如果您查看堆栈跟踪,它会指出您在主应用程序线程上进行网络 I/O 的确切行。
很可能,它来自您的 Downloader()
方法,因为您肯定在该方法中进行网络 I/O,并且该方法是从 onItemClick()
调用的,它本身被称为在主应用程序线程上。请在后台线程上执行此下载。
您在 onPostExecute(List<String> soundNames)
中执行了 Downloader(String fileName)
。但是 onPostExecute()
在主线程上执行。只有 doInBackground()
在单独的线程上执行。将 Downloader(String fileName)
的执行移动到 doInBackground()
.
编辑:或创建一个单独的 AsyncTask,它在自己的 doInBackground()
中执行 Downloader(String fileName)
您应该将任何网络移动到 doInBackground 中,以便它在 AsyncTask 的线程中运行
我目前正在开发一个应用程序,该应用程序可以打印来自远程服务器目录的文件列表,并能够单击指定文件并下载它。
到目前为止,这是我所拥有的:
public class MainActivity extends Activity {
private String user = "username";
private String pass = "password";
private String host = "hostname";
private int portNum = 22;
private static final String SFTPWORKINGDIR = "/path/to/file";
private String fileName;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final ListView list = (ListView)findViewById(R.id.choose_sound_listView);
new AsyncTask<Void, Void, List<String>>() {
@Override
protected List<String> doInBackground(Void... params) {
try {
return executeRemoteCommand(user, pass, host, portNum);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
@Override
public void onPostExecute(List<String> soundNames){
ArrayAdapter<String> adapter = new ArrayAdapter<String>(MainActivity.this,android.R.layout.simple_expandable_list_item_1, android.R.id.text1, soundNames);
list.setAdapter(adapter);
list.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> arg0, View arg1,
int position, long arg3) {
String newFileName = (String) list.getItemAtPosition(position);
fileName = newFileName;
Downloader(fileName);
// Show Alert
Toast.makeText(getApplicationContext(),
"Downloaded " + fileName , Toast.LENGTH_LONG).show();
}
});
}
}.execute();
}
public List<String> executeRemoteCommand(String username, String password, String hostname, int port) throws Exception {
List<String> soundNames = new ArrayList<String>();
JSch jsch = new JSch();
Session session = jsch.getSession(username, hostname, port);
session.setPassword(password);
// Avoid asking for key confirmation
Properties prop = new Properties();
prop.put("StrictHostKeyChecking", "no");
session.setConfig(prop);
session.connect();
String command = "ls -la | awk '{ print }'";
Channel channel = session.openChannel("exec");
((ChannelExec) channel).setCommand(command);
((ChannelExec) channel).setErrStream(System.err);
InputStream in = channel.getInputStream();
System.out.println("Connect to session...");
channel.connect();
StringBuffer buffer = new StringBuffer();
//This is the recommended way to read the output
byte[] tmp = new byte[1024];
while (true) {
while (in.available() > 0) {
int i = in.read(tmp, 0, 1024);
if (i < 0) {
break;
}
buffer.append(new String(tmp, 0, i));
}
if (channel.isClosed()) {
System.out.println("exit-status: " + channel.getExitStatus());
break;
}
try {
Thread.sleep(1000);
} catch (Exception ee) {
}
}
channel.disconnect();
session.disconnect();
Scanner scanner = new Scanner(buffer.toString());
while (scanner.hasNextLine()){
String line = scanner.nextLine();
soundNames.add(line);
Log.i("SSH", ": " + line);
}
return soundNames;
}
public void Downloader(String fileName) {
JSch jsch = new JSch();
Session session = null;
Channel channel = null;
ChannelSftp sftpChannel = null;
try {
session = jsch.getSession(user, host, portNum);
session.setConfig("StrictHostKeyChecking", "no");
session.setPassword(pass);
session.connect();
channel = session.openChannel("sftp");
channel.connect();
sftpChannel = (ChannelSftp) channel;
sftpChannel.cd(SFTPWORKINGDIR); //cd to dir that contains file
try {
byte[] buffer = new byte[1024];
BufferedInputStream bis = new BufferedInputStream(sftpChannel.get(fileName));
File path = Environment.getExternalStoragePublicDirectory(
Environment.DIRECTORY_DOCUMENTS);
File newFile = new File(path, "/" + fileName);
Log.d("Dir" , " " + newFile);
OutputStream os = new FileOutputStream(newFile);
BufferedOutputStream bos = new BufferedOutputStream(os);
int readCount;
while( (readCount = bis.read(buffer)) > 0) {
Log.d("Downloading", " " + fileName );
bos.write(buffer, 0, readCount);
}
bis.close();
bos.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
Log.d( "SUCCESS ", fileName + " has been downloaded!!!!");
sftpChannel.exit();
session.disconnect();
} catch (JSchException e) {
e.printStackTrace();
} catch (SftpException e) {
e.printStackTrace();
}
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.menu_main, menu);
return true;
}
}
但是,当我在 listView 中单击该文件时,出现跟踪错误:
com.jcraft.jsch.JSchException: android.os.NetworkOnMainThreadException
编辑: 运行 它在后台使用 AsyncTask 似乎修复了错误,但如果我这样做,应用程序将无法下载指定的file onClick,除非我硬编码文件的名称。
有人知道我该如何解决这个问题吗?
您正在主应用程序线程上执行网络 I/O,如异常所示。如果您查看堆栈跟踪,它会指出您在主应用程序线程上进行网络 I/O 的确切行。
很可能,它来自您的 Downloader()
方法,因为您肯定在该方法中进行网络 I/O,并且该方法是从 onItemClick()
调用的,它本身被称为在主应用程序线程上。请在后台线程上执行此下载。
您在 onPostExecute(List<String> soundNames)
中执行了 Downloader(String fileName)
。但是 onPostExecute()
在主线程上执行。只有 doInBackground()
在单独的线程上执行。将 Downloader(String fileName)
的执行移动到 doInBackground()
.
编辑:或创建一个单独的 AsyncTask,它在自己的 doInBackground()
Downloader(String fileName)
您应该将任何网络移动到 doInBackground 中,以便它在 AsyncTask 的线程中运行