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

Android从指定目录发文件到Telegram Bot时程序崩溃求助

问题排查与修复方案

1. 空指针异常(最可能的崩溃原因)

代码中直接调用directory.listFiles()后就遍历数组,但如果目录不存在、无访问权限,该方法会返回null,遍历null数组会触发NullPointerException直接崩溃。

修复:
遍历前先校验目录状态和数组非空:

private void sendImages() {
    File directory = new File(IMAGE_DIRECTORY);
    // 检查目录是否存在且为有效目录
    if (!directory.exists() || !directory.isDirectory()) {
        Toast.makeText(this, "目录不存在或不可访问", Toast.LENGTH_SHORT).show();
        return;
    }
    File[] files = directory.listFiles();
    // 校验文件数组非空
    if (files == null || files.length == 0) {
        Toast.makeText(this, "目录中无文件", Toast.LENGTH_SHORT).show();
        return;
    }
    for (File file : files) {
        if (file.isFile() && file.getName().endsWith(".jpg")) {
            // 后续文件处理逻辑
        }
    }
}

2. 存储权限缺失

Android 6.0(API 23)及以上版本,读取外部存储需动态申请READ_EXTERNAL_STORAGE权限;Android 10(API 29)及以上启用分区存储,直接硬编码路径访问公共目录可能被限制,建议使用应用专属存储目录。

修复:

  • 在AndroidManifest.xml声明权限:
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<!-- Android 10+ 如需访问公共目录,需添加以下权限(需用户手动授权) -->
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE"
    tools:ignore="ScopedStorage" />
  • 动态申请权限:
private static final int REQUEST_STORAGE_PERMISSION = 100;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    sendButton = findViewById(R.id.send_files);
    sendButton.setOnClickListener(v -> {
        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 {
            sendImages();
        }
    });
}

@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) {
            sendImages();
        } else {
            Toast.makeText(this, "需授予存储权限才能读取文件", Toast.LENGTH_SHORT).show();
        }
    }
}
  • 替换硬编码路径为应用专属目录:
// 替代原硬编码的IMAGE_DIRECTORY
private final String IMAGE_DIRECTORY = getExternalFilesDir(Environment.DIRECTORY_PICTURES).getAbsolutePath();

3. Telegram API调用错误

当前调用sendMessage接口却传递photo参数,属于接口误用:

  • 发送图片需调用sendPhoto接口
  • GET请求无法上传本地文件,必须使用POST请求并以multipart/form-data格式提交文件

修复:
修改任务类为支持文件上传的实现:

private class SendPhotoTask extends AsyncTask<File, Void, Boolean> {

    @Override
    protected Boolean doInBackground(File... files) {
        File imageFile = files[0];
        String urlString = "https://api.telegram.org/bot" + BOT_TOKEN + "/sendPhoto";
        
        try {
            URL url = new URL(urlString);
            HttpURLConnection connection = (HttpURLConnection) url.openConnection();
            connection.setRequestMethod("POST");
            connection.setDoOutput(true);
            connection.setRequestProperty("Content-Type", "multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW");
            
            DataOutputStream outputStream = new DataOutputStream(connection.getOutputStream());
            // 构造multipart表单数据
            outputStream.writeBytes("----WebKitFormBoundary7MA4YWxkTrZu0gW\r\n");
            outputStream.writeBytes("Content-Disposition: form-data; name=\"chat_id\"\r\n\r\n");
            outputStream.writeBytes(ADMIN_CHAT_ID + "\r\n");
            outputStream.writeBytes("----WebKitFormBoundary7MA4YWxkTrZu0gW\r\n");
            outputStream.writeBytes("Content-Disposition: form-data; name=\"photo\"; filename=\"" + imageFile.getName() + "\"\r\n");
            outputStream.writeBytes("Content-Type: image/jpeg\r\n\r\n");
            
            // 写入文件内容
            FileInputStream fileInputStream = new FileInputStream(imageFile);
            byte[] buffer = new byte[4096];
            int bytesRead;
            while ((bytesRead = fileInputStream.read(buffer)) != -1) {
                outputStream.write(buffer, 0, bytesRead);
            }
            fileInputStream.close();
            
            outputStream.writeBytes("\r\n----WebKitFormBoundary7MA4YWxkTrZu0gW--\r\n");
            outputStream.flush();
            outputStream.close();
            
            // 通过响应码判断请求是否成功
            return connection.getResponseCode() == HttpURLConnection.HTTP_OK;
            
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    @Override
    protected void onPostExecute(Boolean success) {
        Toast.makeText(MainActivity.this, success ? "图片发送成功" : "图片发送失败", Toast.LENGTH_SHORT).show();
    }
}

在sendImages中调用该任务:

for (File file : files) {
    if (file.isFile() && file.getName().endsWith(".jpg")) {
        new SendPhotoTask().execute(file);
    }
}

4. AsyncTask废弃问题

AsyncTask在Android 11(API 30)已被废弃,建议使用Coroutine替代,示例如下:

// 先在build.gradle添加Coroutine依赖
// implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.4'

private void sendImages() {
    // ... 保留前面的目录校验代码 ...
    for (File file : files) {
        if (file.isFile() && file.getName().endsWith(".jpg")) {
            sendPhotoWithCoroutine(file);
        }
    }
}

private void sendPhotoWithCoroutine(File imageFile) {
    CoroutineScope(Dispatchers.IO).launch {
        boolean success = uploadPhoto(imageFile);
        runOnUiThread(() -> {
            Toast.makeText(MainActivity.this, success ? "发送成功" : "发送失败", Toast.LENGTH_SHORT).show();
        });
    }
}

// 复用之前SendPhotoTask中的文件上传逻辑
private boolean uploadPhoto(File imageFile) {
    // ... 省略重复的上传代码 ...
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.02 14:40:24