You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android端使用JSch SFTP上传DCIM文件失败排查及方案咨询

让我帮你排查下问题,你遇到的上传失败大概率是几个Android+JSch的常见坑导致的,咱们一步步来解决:

问题排查与解决方案

1. 动态权限申请缺失(最可能的核心原因)

你在AndroidManifest.xml里声明了READ_EXTERNAL_STORAGE,但你的Build SDK是24(对应Android 7.0),而从Android 6.0(API23)开始,危险权限必须动态申请——仅仅在Manifest里声明是不够的,系统不会自动授予这类权限,必须在运行时向用户主动请求。

解决方法:
在启动上传任务前,先检查并请求权限:

private static final int REQUEST_STORAGE_PERMISSION = 100;

// 在调用上传任务前执行权限检查
if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE)
        != PackageManager.PERMISSION_GRANTED) {
    ActivityCompat.requestPermissions(this,
            new String[]{Manifest.permission.READ_EXTERNAL_STORAGE},
            REQUEST_STORAGE_PERMISSION);
} else {
    // 权限已授予,执行上传任务
    new UploadTask().execute();
}

// 处理权限请求结果
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
    super.onRequestPermissionsResult(requestCode, permissions, grantResults);
    if (requestCode == REQUEST_STORAGE_PERMISSION) {
        if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
            new UploadTask().execute();
        } else {
            Toast.makeText(this, "需要存储权限才能上传图片", Toast.LENGTH_SHORT).show();
        }
    }
}

2. 异常处理的错误操作

你在doInBackground里直接调用Toast.makeText是错误的!doInBackground运行在后台线程,而Toast必须在UI线程执行,这会触发异常但你没捕获到有效日志。另外ex.printStackTrace()在Android环境下可能不会输出到Logcat的可见位置,应该用Android的Log类打印完整异常栈。

修改异常处理部分:

catch (Exception ex) {
    Log.e("SFTP_UPLOAD_ERROR", "上传失败详情", ex); // 打印完整异常信息到Logcat
    // 回到UI线程显示提示
    runOnUiThread(() -> 
        Toast.makeText(getApplicationContext(), "上传失败:" + ex.getMessage(), Toast.LENGTH_LONG).show()
    );
}

这样你就能在Logcat里看到具体错误原因(比如权限拒绝、文件不存在、连接超时等)。

3. 硬编码文件路径不可靠

直接写/storage/emulated/0/DCIM/Camera/...或者/sdcard/...是不推荐的,不同设备的外部存储路径可能存在差异。应该用Android官方API获取标准路径:

// 获取DCIM/Camera的正确路径
File cameraDir = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM), "Camera");
File imageFile = new File(cameraDir, "IMG_20180118_122224.jpg");
String localFilePath = imageFile.getAbsolutePath();

这能保证在绝大多数Android设备上都能定位到正确的图片文件。

4. 未关闭资源导致的潜在问题

你的代码没有关闭ChannelSftp、Channel和Session,这会导致资源泄漏,甚至后续连接失败。应该在finally块里统一关闭这些资源:

finally {
    if (channelSftp2 != null && channelSftp2.isConnected()) {
        channelSftp2.disconnect();
    }
    if (channel2 != null && channel2.isConnected()) {
        channel2.disconnect();
    }
    if (session2 != null && session2.isConnected()) {
        session2.disconnect();
    }
}

完整优化后的代码示例

整合所有修改后的上传任务代码如下:

private class UploadTask extends AsyncTask<Void, Void, Boolean> {

    @Override
    protected Boolean doInBackground(Void... params) {
        String SFTPHOST2 = "192.168.0.1";
        int SFTPPORT2 = 22;
        String SFTPUSER2 = "pi";
        String SFTPPASS2 = "raspberry";
        String SFTPWORKINGDIR2 = "/home/pi/";

        Session session2 = null;
        Channel channel2 = null;
        ChannelSftp channelSftp2 = null;

        try {
            // 获取正确的本地图片路径
            File cameraDir = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM), "Camera");
            File imageFile = new File(cameraDir, "IMG_20180118_122224.jpg");
            if (!imageFile.exists()) {
                Log.e("SFTP_UPLOAD", "图片文件不存在:" + imageFile.getAbsolutePath());
                return false;
            }

            JSch jsch2 = new JSch();
            session2 = jsch2.getSession(SFTPUSER2, SFTPHOST2, SFTPPORT2);
            session2.setPassword(SFTPPASS2);
            session2.setConfig("StrictHostKeyChecking", "no");
            session2.setTimeout(10000);
            session2.connect();

            channel2 = session2.openChannel("sftp");
            channel2.connect();
            channelSftp2 = (ChannelSftp) channel2;
            channelSftp2.cd(SFTPWORKINGDIR2);

            // 执行上传
            channelSftp2.put(imageFile.getAbsolutePath(), imageFile.getName());
            return true;

        } catch (Exception ex) {
            Log.e("SFTP_UPLOAD_ERROR", "上传失败详情", ex);
            return false;
        } finally {
            // 关闭资源
            if (channelSftp2 != null && channelSftp2.isConnected()) {
                channelSftp2.disconnect();
            }
            if (channel2 != null && channel2.isConnected()) {
                channel2.disconnect();
            }
            if (session2 != null && session2.isConnected()) {
                session2.disconnect();
            }
        }
    }

    @Override
    protected void onPostExecute(Boolean success) {
        super.onPostExecute(success);
        if (success) {
            Toast.makeText(getApplicationContext(), "上传成功", Toast.LENGTH_LONG).show();
        } else {
            Toast.makeText(getApplicationContext(), "上传失败,请查看Logcat获取详细信息", Toast.LENGTH_LONG).show();
        }
    }
}

内容的提问来源于stack exchange,提问作者Omid1989

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 08:43:23