使用 SSH 将文件从 android 应用程序发送到 raspberry pi

Sending a file from android application to raspberry pi using SSH

我正在尝试通过 WIFI 从 android 应用程序向 RaspberryPi 发送文件。

我可以连接到 RPI 并通过 SSH 向它发送命令。

这是我用来发送 ssh 命令的函数:

public static String executeRemoteCommand(final String username, final String password, final String hostname, final int port, final File file)
            throws Exception {

        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();

        // SSH Channel
        ChannelExec channelssh = (ChannelExec)
                session.openChannel("exec");
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        channelssh.setOutputStream(baos);

        // Execute command

        channelssh.setCommand("scp " + file + " " + username + "@" + hostname + ":/home/pi/Desktop");
        channelssh.connect();
        try{Thread.sleep(5000);}catch(Exception ee){}
        channelssh.disconnect();
        return baos.toString();
    }

这是要发送的 SSH 命令

scp /storage/emulated/0/Download/201906071017.txt pi@192.168.4.1:/home/pi/Desktop

我在 windows 终端上尝试 运行 SSH 命令并成功上传文件

编辑:

我在 .connect().disconnect() 之间添加了这段代码,以等待我收到命令的响应以断开与 RPi 的连接

        channelssh.setErrStream(System.err);

        InputStream in=channelssh.getInputStream();

        channelssh.connect();

        byte[] tmp=new byte[1024];
        while(true){
            while(in.available()>0){
                int i=in.read(tmp, 0, 1024);
                if(i<0)break;
                System.out.print(new String(tmp, 0, i));
            }
            if(channelssh.isClosed()){
                if(in.available()>0) continue;
                System.out.println("exit-status: "+channelssh.getExitStatus());
                break;
            }
            try{Thread.sleep(1000);}catch(Exception ee){}
        }

        channelssh.disconnect();

现在我的权限在日志中被拒绝,用户名和密码是正确的。

W/System.err: Permission denied, please try again.
W/System.err: Permission denied, please try again.
W/System.err: Permission denied (publickey,password).
W/System.err: lost connection
I/System.out: exit-status: 1

我做到了!! 我最终使用 sftp 服务上传文件 我正在使用 ChannelSshChannelExec。我应该使用 ChannelSftp

这是工作代码

package com.example.a.sitetool;

import android.util.Log;

import com.jcraft.jsch.Channel;
import com.jcraft.jsch.ChannelExec;
import com.jcraft.jsch.ChannelSftp;
import com.jcraft.jsch.JSch;
import com.jcraft.jsch.Session;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.util.Properties;

public class RpiUpdate {


    public static String executeRemoteCommand(final String username, final String password, final String hostname, final int port, final File file)
            throws Exception {

        Log.d("MainActivity", "Entered executeRemoteCommand");

        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();

        // SSH Channel
        Channel channel = session.openChannel("sftp");
        channel.connect();
        ChannelSftp channelsftp = (ChannelSftp) channel;

        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        channelsftp.cd("/home/pi/Desktop");
        channelsftp.put(new FileInputStream(file), file.getName());
        channelsftp.setOutputStream(baos);

        // Execute command
        channelsftp.put(file.getName(),"/home/pi/Desktop");

        InputStream in=channelsftp.getInputStream();

        byte[] tmp=new byte[1024];
        while(true){
            while(in.available()>0){
                int i=in.read(tmp, 0, 1024);
                if(i<0)break;
                System.out.print(new String(tmp, 0, i));
            }
            if(channelsftp.isClosed()){
                if(in.available()>0) continue;
                System.out.println("exit-status: "+channelsftp.getExitStatus());
                break;
            }
            try{Thread.sleep(1000);}catch(Exception ee){}
        }

        channel.disconnect();

        return baos.toString();
    }
}

编辑: 我优化了代码并删除了 ByteArrayOutputStream,因为 Channelsftp 不需要它。此更改导致 return 变为 void 而不是 String 我还添加了更改输出文件权限的功能。

package com.example.a.sitetool;

import android.os.AsyncTask;
import android.os.Environment;
import android.util.Log;

import com.jcraft.jsch.Channel;
import com.jcraft.jsch.ChannelSftp;
import com.jcraft.jsch.JSch;
import com.jcraft.jsch.Session;

import java.io.File;
import java.io.FileInputStream;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
import java.util.Properties;

public class RpiUpdate {
    private static boolean success;


    public static void update() {
        //TODO add online version checking and download
        if (success)
            uploadFile();

    }

    private static void uploadFile() {
        Log.d("RPIUpdate", "Entered upload File");
        success=false;
        new AsyncTask<Integer, Void, Void>() {
            @Override
            protected Void doInBackground(Integer... params) {
                try {
                    executeRemoteCommand("pi", "rbs", "192.168.4.1", 22);
                    Log.d("MainActivity", "File Uploaded");
                    success = true;
                } catch (Exception e) {
                    e.printStackTrace();
                    Log.d("MainActivity", "Failed to upload file");
                    success = false;
                }
                return null;
            }
        }.execute(1);
    }


public static void executeRemoteCommand(final String username, final String password, final String hostname, final int port)
            throws Exception {
        Log.d("RPiUpdate", "Entered executeRemoteCommand");

        //Creating the path on android to bbserver.py
        File root;
        root = new File(Environment.getExternalStorageDirectory() + File.separator + "Download");
        if (!root.exists()) {
            root.mkdirs();
        }
        File file = new File(root, "bbserver.py");

        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();

        //SFTP setup
        Channel channel = session.openChannel("sftp");
        channel.connect();

        ChannelSftp channelsftp = (ChannelSftp) channel;
        //Renaming the old bbserver.py
        SimpleDateFormat formatter = new SimpleDateFormat("yyyy:MM:dd:HH:mm", Locale.ENGLISH);
        Date now = new Date();
        channelsftp.cd("/home/pi/Desktop");
        // 384 in octal is 600. This makes the file non executable. only readable and writable

        channelsftp.chmod(384, "/home/pi/Desktop/bbserver.py");

        channelsftp.rename("/home/pi/Desktop/bbserver.py", "/home/pi/Desktop/bbserver" + formatter.format(now) + ".py");

        //sending the new bbserver.py file
        channelsftp.put(new FileInputStream(file), file.getName());
        Log.d("RPiUpdate", "Sent file");

        //511 in octal is 777. This gives the file all available permissions(read, write, execute). thus making the file an executable

        channelsftp.chmod(511, "/home/pi/Desktop/" + file.getName());
        Log.d("RPiUpdate", "Changed permissions");

        channel.disconnect();

    }