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

Android:多CSV文件导入SQLite多张表的代码问题求助

问题排查与修复方案

嘿,你的importaTabelas方法踩了几个Android SQLite和文件操作的常见坑,我帮你拆解并修复了:

核心问题分析

  1. 数据库连接过早关闭:你在创建表之后直接调用db.close(),导致后面插入数据时数据库已经关闭,这是最致命的错误——相当于刚开门就锁上,根本没法往里塞数据!
  2. SQL拼接的安全与语法问题:直接把CSV行内容拼进INSERT语句,如果行里有单引号(比如O'Conner),会直接触发SQL语法错误,还存在SQL注入风险。
  3. 资源泄漏:FileReader和BufferedReader用完没关闭,会浪费系统资源。
  4. 权限授权无后续处理:申请权限后,没有处理授权成功的回调,用户点允许后方法不会自动重新跑起来。
  5. 路径拼接不规范:用字符串硬拼路径,容易因为系统路径分隔符差异导致找不到文件。

修复后的完整代码

@TargetApi(Build.VERSION_CODES.M)
public void importaTabelas() {
    // 检查读取权限
    if (checkSelfPermission(Manifest.permission.READ_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) {
        try {
            // 初始化目标文件夹
            File downloadsDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS);
            File importDir = new File(downloadsDir, "ENEL/IMPORTADOS");
            if (!importDir.exists()) {
                importDir.mkdirs();
            }

            // 获取文件夹下的所有文件
            File[] files = importDir.listFiles();
            if (files == null) {
                Toast.makeText(this, "文件夹为空或无法访问", Toast.LENGTH_SHORT).show();
                return;
            }

            // 获取可写数据库连接(整个循环复用一个连接,不要频繁开关)
            SQLiteDatabase db = this.banco.getWritableDatabase();

            for (File f : files) {
                if (f.isFile() && f.getName().toLowerCase().endsWith(".csv")) {
                    // 提取表名(去掉.csv后缀)
                    String nomeArq = f.getName();
                    if (nomeArq.indexOf(".") > 0) {
                        nomeArq = nomeArq.substring(0, nomeArq.lastIndexOf("."));
                    }

                    // 1. 创建表(如果不存在)
                    String criaTab = "CREATE TABLE IF NOT EXISTS " + nomeArq + " (id integer PRIMARY KEY AUTOINCREMENT, codigo varchar (50))";
                    db.execSQL(criaTab);

                    // 2. 读取CSV文件内容并插入
                    BufferedReader buffer = null;
                    try {
                        buffer = new BufferedReader(new FileReader(f));
                        String line;
                        // 使用事务提升插入效率
                        db.beginTransaction();
                        while ((line = buffer.readLine()) != null) {
                            // 跳过空行
                            if (line.trim().isEmpty()) continue;
                            // 使用参数化查询避免SQL注入和语法错误
                            String insertSql = "INSERT INTO " + nomeArq + " (codigo) VALUES (?)";
                            db.execSQL(insertSql, new Object[]{line});
                        }
                        db.setTransactionSuccessful();
                        Toast.makeText(this, "表 " + nomeArq + " 导入完成", Toast.LENGTH_SHORT).show();
                    } catch (IOException e) {
                        e.printStackTrace();
                        Toast.makeText(this, "读取文件 " + f.getName() + " 失败", Toast.LENGTH_SHORT).show();
                    } finally {
                        // 确保流被关闭
                        if (buffer != null) {
                            try {
                                buffer.close();
                            } catch (IOException e) {
                                e.printStackTrace();
                            }
                        }
                    }
                }
            }

            // 所有操作完成后再关闭数据库
            if (db.isOpen()) {
                db.close();
            }

        } catch (Exception e) {
            Toast.makeText(this, "导入过程出错: " + e.getMessage(), Toast.LENGTH_SHORT).show();
            e.printStackTrace();
        }
    } else {
        // 请求读取外部存储权限
        requestPermissions(new String[]{Manifest.permission.READ_EXTERNAL_STORAGE}, 1);
    }
}

// 必须重写这个方法处理权限授权结果
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
    super.onRequestPermissionsResult(requestCode, permissions, grantResults);
    if (requestCode == 1) {
        if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
            // 授权成功后重新执行导入方法
            importaTabelas();
        } else {
            Toast.makeText(this, "需要读取存储权限才能导入文件", Toast.LENGTH_SHORT).show();
        }
    }
}

关键改动说明

  • 复用数据库连接:整个循环只打开一次数据库,所有操作完成后再关闭,避免频繁IO和连接关闭后的操作失败。
  • 参数化SQL查询:用?占位符代替字符串拼接,彻底解决SQL注入和单引号导致的语法错误。
  • 添加事务处理:批量插入时用事务包裹,大幅提升插入效率,避免部分插入失败导致的数据不一致。
  • 资源自动释放:用finally块确保流被关闭,避免资源泄漏。
  • 完善权限回调:重写onRequestPermissionsResult,授权成功后自动重新执行导入逻辑。
  • 规范路径拼接:用File构造方法拼接路径,适配不同系统的路径分隔符。
  • 增加空值检查:判断listFiles()的返回值,避免空指针异常。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.12 04:06:31