14

I am trying to export SQLite data to SD card in android as a CSV file on a directory.

So i have tried this method below and apparently it only shows this text printed out:

FIRST TABLE OF THE DATABASE
DATE,ITEM,AMOUNT,CURRENCY

In my DBHelper.java i have defined the function as follows:

public boolean exportDatabase() {
        DateFormat df = DateFormat.getDateInstance(DateFormat.SHORT, Locale.getDefault());

        /**First of all we check if the external storage of the device is available for writing.
         * Remember that the external storage is not necessarily the sd card. Very often it is
         * the device storage.
         */
        String state = Environment.getExternalStorageState();
        if (!Environment.MEDIA_MOUNTED.equals(state)) {
            return false;
        }
        else {
            //We use the Download directory for saving our .csv file.
            File exportDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS);
            if (!exportDir.exists())
            {
                exportDir.mkdirs();
            }

            File file;
            PrintWriter printWriter = null;
            try
            {
                file = new File(exportDir, "MyCSVFile.csv");
                file.createNewFile();
                printWriter = new PrintWriter(new FileWriter(file));

                /**This is our database connector class that reads the data from the database.
                 * The code of this class is omitted for brevity.
                 */
                SQLiteDatabase db = this.getReadableDatabase(); //open the database for reading

                /**Let's read the first table of the database.
                 * getFirstTable() is a method in our DBCOurDatabaseConnector class which retrieves a Cursor
                 * containing all records of the table (all fields).
                 * The code of this class is omitted for brevity.
                 */
                Cursor curCSV = db.rawQuery("select * from contacts", null);
                //Write the name of the table and the name of the columns (comma separated values) in the .csv file.
                printWriter.println("FIRST TABLE OF THE DATABASE");
                printWriter.println("DATE,ITEM,AMOUNT,CURRENCY");
                while(curCSV.moveToNext())
                {
                    Long date = curCSV.getLong(curCSV.getColumnIndex("date"));
                    String title = curCSV.getString(curCSV.getColumnIndex("title"));
                    Float amount = curCSV.getFloat(curCSV.getColumnIndex("amount"));
                    String description = curCSV.getString(curCSV.getColumnIndex("description"));

                    /**Create the line to write in the .csv file.
                     * We need a String where values are comma separated.
                     * The field date (Long) is formatted in a readable text. The amount field
                     * is converted into String.
                     */
                    String record = df.format(new Date(date)) + "," + title + "," + amount + "," + description;
                    printWriter.println(record); //write the record in the .csv file
                }

                curCSV.close();
                db.close();
            }

            catch(Exception exc) {
                //if there are any exceptions, return false
                return false;
            }
            finally {
                if(printWriter != null) printWriter.close();
            }

            //If there are no errors, return true.
            return true;
        }
    }
}

And my columns are:

 public static final String DATABASE_NAME = "MyDBName.db";
 public static final String CONTACTS_TABLE_NAME = "contacts";
 public static final String CONTACTS_COLUMN_ID = "id";
 public static final String CONTACTS_COLUMN_TITLE = "title";
 public static final String CONTACTS_COLUMN_AMOUNT = "amount";
 public static final String CONTACTS_COLUMN_DESC = "description";

Let me know if you need more code.

Phantômaxx
  • 37,901
  • 21
  • 84
  • 115
Steve Kamau
  • 2,755
  • 10
  • 42
  • 73
  • what do you want? are you getting any error. – RajSharma Jul 12 '15 at 11:42
  • 1
    `//Write the name of the table and the name of the columns (comma separated values) in the .csv file.` Then, it's no more a valid CSV file, if you add the table name. The first line in a valid CSV file either is a header row with the column names or it's just the first data row. – Phantômaxx Jul 12 '15 at 12:00
  • For what concerns your issue... it seems like there's no data in the table. – Phantômaxx Jul 12 '15 at 12:03
  • @CodeProcessor nope i am not getting any error,the CSV file just conatins the headers and not the sqlite data itself – Steve Kamau Jul 12 '15 at 12:17
  • @DerGol...lum there is data in the table because i can show the data in a listview.. – Steve Kamau Jul 12 '15 at 12:18
  • 1
    Try setting the PrintWriter AutoFlush parameter to true. `printWriter = new PrintWriter(new FileWriter(file), true);` – Phantômaxx Jul 12 '15 at 12:25

3 Answers3

35

Thanks for your suggestions guys which led me to this answer:

private void exportDB() {

        DBHelper dbhelper = new DBHelper(getApplicationContext());
        File exportDir = new File(Environment.getExternalStorageDirectory(), "");
        if (!exportDir.exists())
        {
            exportDir.mkdirs();
        }

        File file = new File(exportDir, "csvname.csv");
        try
        {
            file.createNewFile();
            CSVWriter csvWrite = new CSVWriter(new FileWriter(file));
            SQLiteDatabase db = dbhelper.getReadableDatabase();
            Cursor curCSV = db.rawQuery("SELECT * FROM contacts",null);
            csvWrite.writeNext(curCSV.getColumnNames());
            while(curCSV.moveToNext())
            {
                //Which column you want to exprort
                String arrStr[] ={curCSV.getString(0),curCSV.getString(1), curCSV.getString(2)};
                csvWrite.writeNext(arrStr);
            }
            csvWrite.close();
            curCSV.close();
        }
        catch(Exception sqlEx)
        {
            Log.e("MainActivity", sqlEx.getMessage(), sqlEx);
        }
}
Alaa M.
  • 4,961
  • 10
  • 54
  • 95
Steve Kamau
  • 2,755
  • 10
  • 42
  • 73
  • 4
    Not so clear where to get `getDatabasePath`, `DBHelper`, `CSVWriter` and other parts of this code. – zygimantus Jan 31 '17 at 21:07
  • 1
    `getDatabasePath()` method finds the path of the database location, `DBHelper` is my sqlite handler class which extends `sqliteOpenHelper`, `CSVWriter` is the CSV function. The code works directly without much hustle. – Steve Kamau Feb 01 '17 at 07:00
  • 2
    A pity that you're not sharing those classes here. They could be useful for others. – leoneboaventura Mar 22 '17 at 07:54
  • 6
    CSVWriter can be found here : https://github.com/rogerta/secrets-for-android/tree/master/app/src/main/java/au/com/bytecode/opencsv – Dranna Apr 07 '17 at 13:24
  • 1
    @SteveKamau what if I want all the data. – Krishna Murari Oct 17 '18 at 10:42
  • Use @Pete suggestion below, specifically this method getTablesOnDataBase(..) – Steve Kamau Oct 18 '18 at 07:29
  • thanks this works very well but it lock the database not allowing any other database operation after – Cleaton Pais Mar 17 '21 at 11:28
  • @CleatonPais Take into account that SQLite databases are file based and are not intended to be able to be accessed in a multi-process way. The best procedure on mixing SQLite with multi-processing is using semaphores (aquire(), release()) in each database related access. – Steve Kamau Mar 17 '21 at 12:50
12

In case someone still stumbles upon this question, I will post my solution, which is slightly more general than the accepted one. You should be able to export all tables in an sqlite database almost by copying the two classes below. Only other changes needed should be related to getting the application context and importing open csv.

Some parts are pretty much copy paste from other stackoverflow threads, but I couldn't find those anymore.

Sqlite exporter:

import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.util.Log;

import com.opencsv.CSVWriter;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;


/**
 * Can export an sqlite databse into a csv file.
 *
 * The file has on the top dbVersion and on top of each table data the name of the table
 *
 * Inspired by
 * https://stackoverflow.com/questions/31367270/exporting-sqlite-database-to-csv-file-in-android
 * and some other SO threads as well.
 *
 */
public class SqliteExporter {
    private static final String TAG = SqliteExporter.class.getSimpleName();

    public static final String DB_BACKUP_DB_VERSION_KEY = "dbVersion";
    public static final String DB_BACKUP_TABLE_NAME = "table";

    public static String export(SQLiteDatabase db) throws IOException{
        if( !FileUtils.isExternalStorageWritable() ){
            throw new IOException("Cannot write to external storage");
        }
        File backupDir = FileUtils.createDirIfNotExist(FileUtils.getAppDir() + "/backup");
        String fileName = createBackupFileName();
        File backupFile = new File(backupDir, fileName);
        boolean success = backupFile.createNewFile();
        if(!success){
            throw new IOException("Failed to create the backup file");
        }
        List<String> tables = getTablesOnDataBase(db);
        Log.d(TAG, "Started to fill the backup file in " + backupFile.getAbsolutePath());
        long starTime = System.currentTimeMillis();
        writeCsv(backupFile, db, tables);
        long endTime = System.currentTimeMillis();
        Log.d(TAG, "Creating backup took " + (endTime - starTime) + "ms.");

        return backupFile.getAbsolutePath();
    }

    private static String createBackupFileName(){
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd_HHmm");
        return "db_backup_" + sdf.format(new Date()) + ".csv";
    }

    /**
     * Get all the table names we have in db
     *
     * @param db
     * @return
     */
    public static List<String> getTablesOnDataBase(SQLiteDatabase db){
        Cursor c = null;
        List<String> tables = new ArrayList<>();
        try{
            c = db.rawQuery("SELECT name FROM sqlite_master WHERE type='table'", null);
            if (c.moveToFirst()) {
                while ( !c.isAfterLast() ) {
                    tables.add(c.getString(0));
                    c.moveToNext();
                }
            }
        }
        catch(Exception throwable){
            Log.e(TAG, "Could not get the table names from db", throwable);
        }
        finally{
            if(c!=null)
                c.close();
        }
        return tables;
    }

    private static void writeCsv(File backupFile, SQLiteDatabase db, List<String> tables){
        CSVWriter csvWrite = null;
        Cursor curCSV = null;
        try {
            csvWrite = new CSVWriter(new FileWriter(backupFile));
            writeSingleValue(csvWrite, DB_BACKUP_DB_VERSION_KEY + "=" + db.getVersion());
            for(String table: tables){
                writeSingleValue(csvWrite, DB_BACKUP_TABLE_NAME + "=" + table);
                curCSV = db.rawQuery("SELECT * FROM " + table,null);
                csvWrite.writeNext(curCSV.getColumnNames());
                while(curCSV.moveToNext()) {
                    int columns = curCSV.getColumnCount();
                    String[] columnArr = new String[columns];
                    for( int i = 0; i < columns; i++){
                        columnArr[i] = curCSV.getString(i);
                    }
                    csvWrite.writeNext(columnArr);
                }
            }
        }
        catch(Exception sqlEx) {
            Log.e(TAG, sqlEx.getMessage(), sqlEx);
        }finally {
            if(csvWrite != null){
                try {
                    csvWrite.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if( curCSV != null ){
                curCSV.close();
            }
        }
    }

    private static void writeSingleValue(CSVWriter writer, String value){
        writer.writeNext(new String[]{value});
    }
}

FileUtils:

public class FileUtils {

    public static String getAppDir(){
        return App.getContext().getExternalFilesDir(null) + "/" + App.getContext().getString(R.string.app_name);
    }

    public static File createDirIfNotExist(String path){
        File dir = new File(path);
        if( !dir.exists() ){
            dir.mkdir();
        }
        return dir;
    }

    /* Checks if external storage is available for read and write */
    public static boolean isExternalStorageWritable() {
        String state = Environment.getExternalStorageState();
        return Environment.MEDIA_MOUNTED.equals(state);
    }

    /* Checks if external storage is available to at least read */
    public static boolean isExternalStorageReadable() {
        String state = Environment.getExternalStorageState();
        return Environment.MEDIA_MOUNTED.equals(state) ||
                Environment.MEDIA_MOUNTED_READ_ONLY.equals(state);
    }
}

In addition to these two classes, you either need to pass context to the FileUtils, or have some other means of getting the context. In the above code App is just class extending Application for easy access to context.

Also remember to add Opencsv to gradle, i.e.

compile group: 'com.opencsv', name: 'opencsv', version: '4.1'

Then just call the export method of the Sqlite exporter class.

Pete
  • 280
  • 3
  • 7
  • How to call this method from MainActivity? – user2872856 Dec 16 '17 at 01:54
  • You can call it with `SqliteExporter.export(db)` where db is the SQLiteDatabase object, which you can get for example from the sqliteOpenHelper with getReadableDatabase() – Pete Dec 26 '17 at 10:20
  • 1
    You probably want to call the export method from a background thread though, since it can take a while to export the database. For this you may want to create an intentService or something else that does not block the ui thread. – Pete Dec 26 '17 at 11:02
  • 2
    I had to make one slight modification. In the createDirIfNotExist method, i had to change it from "dir.mkdir()" to "dir.mkdirs()", because i was missing multiple parent directories. Other than that, it worked perfectly. – yarell Jun 27 '18 at 17:13
  • Phenomenal answer. I appreciate your time to put in this level of effort. It worked swimmingly, although I changed my query to just dump one table (with all of its association tables joined). – DWndrer Jul 21 '18 at 06:25
  • 1
    It works. I also had to change it from "dir.mkdir()" to "dir.mkdirs()" though. Thanks – Anky An Jun 27 '19 at 20:31
  • 1
    Cannot resolve symbol 'SqliteExporter', what do I have to do? – morteza naji Jun 12 '21 at 14:15
1

First of all remove this line to have a valid csv formatted document.

printWriter.println("FIRST TABLE OF THE DATABASE");

Second, make sure you have data in your table and check if your query actually returns anything by debugging.

Gunhan
  • 6,807
  • 3
  • 43
  • 37