diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/SQLiteViewer.java b/Core/src/org/sleuthkit/autopsy/contentviewers/SQLiteViewer.java
index 40a667e3d7f2d90bf0863a1c3799aaead1b8e675..6988e46527e7e36476b57475005e407fb6f54e76 100644
--- a/Core/src/org/sleuthkit/autopsy/contentviewers/SQLiteViewer.java
+++ b/Core/src/org/sleuthkit/autopsy/contentviewers/SQLiteViewer.java
@@ -22,23 +22,17 @@
 import java.awt.Component;
 import java.awt.Cursor;
 import java.io.File;
+import java.io.FileNotFoundException;
 import java.io.FileOutputStream;
 import java.io.IOException;
-import java.sql.Connection;
-import java.sql.DriverManager;
-import java.sql.ResultSet;
-import java.sql.ResultSetMetaData;
 import java.sql.SQLException;
-import java.sql.Statement;
-import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
-import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Objects;
-import java.util.TreeMap;
 import java.util.logging.Level;
+import java.util.stream.Collectors;
 import javax.swing.JComboBox;
 import javax.swing.JFileChooser;
 import javax.swing.JOptionPane;
@@ -48,14 +42,11 @@
 import org.openide.windows.WindowManager;
 import org.sleuthkit.autopsy.casemodule.Case;
 import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
-import org.sleuthkit.autopsy.casemodule.services.FileManager;
-import org.sleuthkit.autopsy.casemodule.services.Services;
 import org.sleuthkit.autopsy.coreutils.Logger;
-import org.sleuthkit.autopsy.datamodel.ContentUtils;
 import org.sleuthkit.datamodel.AbstractFile;
-import org.sleuthkit.datamodel.SleuthkitCase;
 import org.sleuthkit.datamodel.TskCoreException;
 import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil;
+import org.sleuthkit.autopsy.sqlitereader.SQLiteReader;
 
 /**
  * A file content viewer for SQLite database files.
@@ -70,7 +61,7 @@ class SQLiteViewer extends javax.swing.JPanel implements FileTypeViewer {
     private final SQLiteTableView selectedTableView = new SQLiteTableView();
     private AbstractFile sqliteDbFile;
     private File tmpDbFile;
-    private Connection connection;
+    private SQLiteReader sqliteReader;
     private int numRows;    // num of rows in the selected table
     private int currPage = 0; // curr page of rows being displayed
 
@@ -347,10 +338,10 @@ public void resetComponent() {
         numEntriesField.setText("");
 
         // close DB connection to file
-        if (null != connection) {
+        if (null != sqliteReader) {
             try {
-                connection.close();
-                connection = null;
+                sqliteReader.close();
+                sqliteReader = null;
             } catch (SQLException ex) {
                 logger.log(Level.SEVERE, "Failed to close DB connection to file.", ex); //NON-NLS
             }
@@ -370,41 +361,15 @@ public void resetComponent() {
         "SQLiteViewer.errorMessage.failedToQueryDatabase=The database tables in the file could not be read.",
         "SQLiteViewer.errorMessage.failedToinitJDBCDriver=The JDBC driver for SQLite could not be loaded.",
         "# {0} - exception message", "SQLiteViewer.errorMessage.unexpectedError=An unexpected error occurred:\n{0).",})
-    private void processSQLiteFile() {
-                
+    private void processSQLiteFile() {       
         tablesDropdownList.removeAllItems();
-
-        // Copy the file to temp folder
-        String tmpDBPathName;
         try {
-            tmpDBPathName = Case.getCurrentCaseThrows().getTempDirectory() + File.separator + sqliteDbFile.getName();
-        } catch (NoCurrentCaseException ex) {
-            logger.log(Level.SEVERE, "Current case has been closed", ex); //NON-NLS
-            MessageNotifyUtil.Message.error(Bundle.SQLiteViewer_errorMessage_noCurrentCase());
-            return;
-        }
-
-        tmpDbFile = new File(tmpDBPathName);
-        if (! tmpDbFile.exists()) {
-            try {
-                ContentUtils.writeToFile(sqliteDbFile, tmpDbFile);
-
-                // Look for any meta files associated with this DB - WAL, SHM, etc. 
-                findAndCopySQLiteMetaFile(sqliteDbFile, sqliteDbFile.getName() + "-wal");
-                findAndCopySQLiteMetaFile(sqliteDbFile, sqliteDbFile.getName() + "-shm");
-            } catch (IOException | NoCurrentCaseException | TskCoreException ex) {
-                logger.log(Level.SEVERE, String.format("Failed to create temp copy of DB file '%s' (objId=%d)", sqliteDbFile.getName(), sqliteDbFile.getId()), ex); //NON-NLS
-                MessageNotifyUtil.Message.error(Bundle.SQLiteViewer_errorMessage_failedToExtractFile());
-                return;
-            }
-        }
-                
-        try {
-            // Load the SQLite JDBC driver, if necessary.
-            Class.forName("org.sqlite.JDBC"); //NON-NLS  
-            connection = DriverManager.getConnection("jdbc:sqlite:" + tmpDBPathName); //NON-NLS
-
-            Map<String, String> dbTablesMap = getTables();
+            String localDiskPath = Case.getCurrentCaseThrows().getTempDirectory() + 
+                    File.separator + sqliteDbFile.getName();
+            sqliteReader = new SQLiteReader(sqliteDbFile, localDiskPath);
+            
+            Map<String, String> dbTablesMap = sqliteReader.getTableSchemas();
+            
             if (dbTablesMap.isEmpty()) {
                 tablesDropdownList.addItem(Bundle.SQLiteViewer_comboBox_noTableEntry());
                 tablesDropdownList.setEnabled(false);
@@ -413,78 +378,36 @@ private void processSQLiteFile() {
                     tablesDropdownList.addItem(tableName);
                 });
             }
+        } catch (NoCurrentCaseException ex) {
+            logger.log(Level.SEVERE, "Current case has been closed", ex); //NON-NLS
+            MessageNotifyUtil.Message.error(Bundle.SQLiteViewer_errorMessage_noCurrentCase());
+        } catch (IOException | TskCoreException ex) {
+            logger.log(Level.SEVERE, String.format(
+                    "Failed to create temp copy of DB file '%s' (objId=%d)", //NON-NLS
+                    sqliteDbFile.getName(), sqliteDbFile.getId()), ex);
+            MessageNotifyUtil.Message.error(
+                    Bundle.SQLiteViewer_errorMessage_failedToExtractFile());
         } catch (ClassNotFoundException ex) {
-            logger.log(Level.SEVERE, String.format("Failed to initialize JDBC SQLite '%s' (objId=%d)", sqliteDbFile.getName(), sqliteDbFile.getId()), ex); //NON-NLS
-            MessageNotifyUtil.Message.error(Bundle.SQLiteViewer_errorMessage_failedToinitJDBCDriver());
+            logger.log(Level.SEVERE, String.format(
+                    "Failed to initialize JDBC SQLite '%s' (objId=%d)", //NON-NLS
+                    sqliteDbFile.getName(), sqliteDbFile.getId()), ex);
+            MessageNotifyUtil.Message.error(
+                    Bundle.SQLiteViewer_errorMessage_failedToinitJDBCDriver());
         } catch (SQLException ex) {
-            logger.log(Level.SEVERE, String.format("Failed to get tables from DB file  '%s' (objId=%d)", sqliteDbFile.getName(), sqliteDbFile.getId()), ex); //NON-NLS
-            MessageNotifyUtil.Message.error(Bundle.SQLiteViewer_errorMessage_failedToQueryDatabase());
-        }
-    }
-
-    /**
-     * Searches for a meta file associated with the give SQLite db If found,
-     * copies the file to the temp folder
-     *
-     * @param sqliteFile   - SQLIte db file being processed
-     * @param metaFileName name of meta file to look for
-     */
-    private void findAndCopySQLiteMetaFile(AbstractFile sqliteFile, String metaFileName) throws NoCurrentCaseException, TskCoreException, IOException {
-        Case openCase = Case.getCurrentCaseThrows();
-        SleuthkitCase sleuthkitCase = openCase.getSleuthkitCase();
-        Services services = new Services(sleuthkitCase);
-        FileManager fileManager = services.getFileManager();
-        List<AbstractFile> metaFiles = fileManager.findFiles(sqliteFile.getDataSource(), metaFileName, sqliteFile.getParent().getName());
-        if (metaFiles != null) {
-            for (AbstractFile metaFile : metaFiles) {
-                String tmpMetafilePathName = openCase.getTempDirectory() + File.separator + metaFile.getName();
-                File tmpMetafile = new File(tmpMetafilePathName);
-                ContentUtils.writeToFile(metaFile, tmpMetafile);
-            }
+            logger.log(Level.SEVERE, String.format(
+                    "Failed to get tables from DB file  '%s' (objId=%d)", //NON-NLS
+                    sqliteDbFile.getName(), sqliteDbFile.getId()), ex);
+            MessageNotifyUtil.Message.error(
+                    Bundle.SQLiteViewer_errorMessage_failedToQueryDatabase());
         }
     }
 
-    /**
-     * Gets the table names and schemas from the SQLite database file.
-     *
-     * @return A mapping of table names to SQL CREATE TABLE statements.
-     */
-    private Map<String, String> getTables() throws SQLException {
-        Map<String, String> dbTablesMap = new TreeMap<>();
-        Statement statement = null;
-        ResultSet resultSet = null;
-        try {
-            statement = connection.createStatement();
-            resultSet = statement.executeQuery(
-                    "SELECT name, sql FROM sqlite_master "
-                    + " WHERE type= 'table' "
-                    + " ORDER BY name;"); //NON-NLS
-            while (resultSet.next()) {
-                String tableName = resultSet.getString("name"); //NON-NLS
-                String tableSQL = resultSet.getString("sql"); //NON-NLS
-                dbTablesMap.put(tableName, tableSQL);
-            }
-        } finally {
-            if (null != resultSet) {
-                resultSet.close();
-            }
-            if (null != statement) {
-                statement.close();
-            }
-        }
-        return dbTablesMap;
-    }
-
     @NbBundle.Messages({"# {0} - tableName",
         "SQLiteViewer.selectTable.errorText=Error getting row count for table: {0}"
     })
     private void selectTable(String tableName) {
-
-        try (Statement statement = connection.createStatement();
-             ResultSet resultSet = statement.executeQuery(
-                    "SELECT count (*) as count FROM " + tableName)) { //NON-NLS{
-
-            numRows = resultSet.getInt("count");
+        try {
+            numRows = sqliteReader.getTableRowCount(tableName);
             numEntriesField.setText(numRows + " entries");
 
             currPage = 1;
@@ -504,8 +427,11 @@ private void selectTable(String tableName) {
             }
             
         } catch (SQLException ex) {
-            logger.log(Level.SEVERE, String.format("Failed to load table %s from DB file '%s' (objId=%d)", tableName, sqliteDbFile.getName(), sqliteDbFile.getId()), ex); //NON-NLS
-            MessageNotifyUtil.Message.error(Bundle.SQLiteViewer_selectTable_errorText(tableName));
+            logger.log(Level.SEVERE, String.format(
+                    "Failed to load table %s from DB file '%s' (objId=%d)", tableName, //NON-NLS
+                    sqliteDbFile.getName(), sqliteDbFile.getId()), ex);
+            MessageNotifyUtil.Message.error(
+                    Bundle.SQLiteViewer_selectTable_errorText(tableName));
         }
     }
 
@@ -513,109 +439,108 @@ private void selectTable(String tableName) {
         "SQLiteViewer.readTable.errorText=Error getting rows for table: {0}"})
     private void readTable(String tableName, int startRow, int numRowsToRead) {
 
-        try (
-            Statement statement = connection.createStatement();
-            ResultSet resultSet = statement.executeQuery(
-                    "SELECT * FROM " + tableName
-                    + " LIMIT " + Integer.toString(numRowsToRead)
-                    + " OFFSET " + Integer.toString(startRow - 1))) {
-
-            ArrayList<Map<String, Object>> rows = resultSetToArrayList(resultSet);
+        try {
+            List<Map<String, Object>> rows = sqliteReader.getRowsFromTable(
+                    tableName, startRow, numRowsToRead);
             if (Objects.nonNull(rows)) {
                 selectedTableView.setupTable(rows);
             } else {
                 selectedTableView.setupTable(Collections.emptyList());
             }
         } catch (SQLException ex) {
-            logger.log(Level.SEVERE, String.format("Failed to read table %s from DB file '%s' (objId=%d)", tableName, sqliteDbFile.getName(), sqliteDbFile.getId()), ex); //NON-NLS
-            MessageNotifyUtil.Message.error(Bundle.SQLiteViewer_readTable_errorText(tableName));
+            logger.log(Level.SEVERE, String.format(
+                    "Failed to read table %s from DB file '%s' (objId=%d)", tableName, //NON-NLS
+                    sqliteDbFile.getName(), sqliteDbFile.getId()), ex);
+            MessageNotifyUtil.Message.error(
+                    Bundle.SQLiteViewer_readTable_errorText(tableName));
         }
     }
+    
+    /**
+     * Converts a sqlite table into a CSV file.
+     * 
+     * @param file
+     * @param tableName
+     * @param rowMap -- A list of rows in the table, where each row is represented as a column-value
+     * map.
+     * @throws FileNotFoundException
+     * @throws IOException 
+     */
+    @NbBundle.Messages({
+        "SQLiteViewer.exportTableToCsv.FileName=File name: ",
+        "SQLiteViewer.exportTableToCsv.TableName=Table name: "
+    })
+    public void exportTableToCSV(File file, String tableName, 
+            List<Map<String, Object>> rowMap) throws FileNotFoundException, IOException{
+        
+        File csvFile;
+        String fileName = file.getName();
+        if (FilenameUtils.getExtension(fileName).equalsIgnoreCase("csv")) {
+            csvFile = file;
+        } else {
+            csvFile = new File(file.toString() + ".csv");
+        }
 
-    @NbBundle.Messages("SQLiteViewer.BlobNotShown.message=BLOB Data not shown")
-    private ArrayList<Map<String, Object>> resultSetToArrayList(ResultSet rs) throws SQLException {
-        ResultSetMetaData metaData = rs.getMetaData();
-        int columns = metaData.getColumnCount();
-        ArrayList<Map<String, Object>> rowlist = new ArrayList<>();
-        while (rs.next()) {
-            Map<String, Object> row = new LinkedHashMap<>(columns);
-            for (int i = 1; i <= columns; ++i) {
-                if (rs.getObject(i) == null) {
-                    row.put(metaData.getColumnName(i), "");
-                } else {
-                    if (metaData.getColumnTypeName(i).compareToIgnoreCase("blob") == 0) {
-                        row.put(metaData.getColumnName(i), Bundle.SQLiteViewer_BlobNotShown_message());
-                    } else {
-                        row.put(metaData.getColumnName(i), rs.getObject(i));
-                    }
-                }
+        try (FileOutputStream out = new FileOutputStream(csvFile, false)) {
+
+            out.write((Bundle.SQLiteViewer_exportTableToCsv_FileName() + csvFile.getName() + "\n").getBytes());
+            out.write((Bundle.SQLiteViewer_exportTableToCsv_TableName() + tableName + "\n").getBytes());
+            
+            String header = createColumnHeader(rowMap.get(0)).concat("\n");
+            out.write(header.getBytes());
+
+            for (Map<String, Object> maps : rowMap) {
+                String row = maps.values()
+                        .stream()
+                        .map(Object::toString)
+                        .collect(Collectors.joining(","))
+                        .concat("\n");
+                out.write(row.getBytes());
             }
-            rowlist.add(row);
         }
-
-        return rowlist;
     }
     
-    @NbBundle.Messages({"SQLiteViewer.exportTableToCsv.write.errText=Failed to export table content to csv file.",
-                        "SQLiteViewer.exportTableToCsv.FileName=File name: ",
-                        "SQLiteViewer.exportTableToCsv.TableName=Table name: "
+    @NbBundle.Messages({
+        "SQLiteViewer.exportTableToCsv.write.errText=Failed to export table content to csv file.",
     })
     private void exportTableToCsv(File file) {
         String tableName = (String) this.tablesDropdownList.getSelectedItem();
-        try (
-                Statement statement = connection.createStatement();
-                ResultSet resultSet = statement.executeQuery("SELECT * FROM " + tableName)) {
-            List<Map<String, Object>> currentTableRows = resultSetToArrayList(resultSet);
+        try {
+            List<Map<String, Object>> currentTableRows = 
+                    sqliteReader.getRowsFromTable(tableName);
 
             if (Objects.isNull(currentTableRows) || currentTableRows.isEmpty()) {
-                logger.log(Level.INFO, String.format("The table %s is empty. (objId=%d)", tableName, sqliteDbFile.getId())); //NON-NLS
+                logger.log(Level.INFO, String.format(
+                        "The table %s is empty. (objId=%d)", tableName, //NON-NLS
+                        sqliteDbFile.getId()));
             } else {
-                File csvFile;
-                String fileName = file.getName();
-                if (FilenameUtils.getExtension(fileName).equalsIgnoreCase("csv")) {
-                    csvFile = file;
-                } else {
-                    csvFile = new File(file.toString() + ".csv");
-                }
-
-                try (FileOutputStream out = new FileOutputStream(csvFile, false)) {
-
-                    out.write((Bundle.SQLiteViewer_exportTableToCsv_FileName() + csvFile.getName() + "\n").getBytes());
-                    out.write((Bundle.SQLiteViewer_exportTableToCsv_TableName() + tableName + "\n").getBytes());
-                    // Set up the column names
-                    Map<String, Object> row = currentTableRows.get(0);
-                    StringBuffer header = new StringBuffer();
-                    for (Map.Entry<String, Object> col : row.entrySet()) {
-                        String colName = col.getKey();
-                        if (header.length() > 0) {
-                            header.append(',').append(colName);
-                        } else {
-                            header.append(colName);
-                        }
-                    }
-                    out.write(header.append('\n').toString().getBytes());
-
-                    for (Map<String, Object> maps : currentTableRows) {
-                        StringBuffer valueLine = new StringBuffer();
-                        maps.values().forEach((value) -> {
-                            if (valueLine.length() > 0) {
-                                valueLine.append(',').append(value.toString());
-                            } else {
-                                valueLine.append(value.toString());
-                            }
-                        });
-                        out.write(valueLine.append('\n').toString().getBytes());
-                    }
-                }
+                exportTableToCSV(file, tableName, currentTableRows);
             }
         } catch (SQLException ex) {
-            logger.log(Level.SEVERE, String.format("Failed to read table %s from DB file '%s' (objId=%d)", tableName, sqliteDbFile.getName(), sqliteDbFile.getId()), ex); //NON-NLS
-            MessageNotifyUtil.Message.error(Bundle.SQLiteViewer_readTable_errorText(tableName));
+            logger.log(Level.SEVERE, String.format(
+                    "Failed to read table %s from DB file '%s' (objId=%d)", //NON-NLS
+                    tableName, sqliteDbFile.getName(), sqliteDbFile.getId()), ex); 
+            MessageNotifyUtil.Message.error(
+                    Bundle.SQLiteViewer_readTable_errorText(tableName));
         } catch (IOException ex) {
-            logger.log(Level.SEVERE, String.format("Failed to export table %s to file '%s'", tableName, file.getName()), ex); //NON-NLS
-            MessageNotifyUtil.Message.error(Bundle.SQLiteViewer_exportTableToCsv_write_errText());
+            logger.log(Level.SEVERE, String.format(
+                    "Failed to export table %s to file '%s'", tableName, file.getName()), ex); //NON-NLS
+            MessageNotifyUtil.Message.error(
+                    Bundle.SQLiteViewer_exportTableToCsv_write_errText());
         }
     }
-
     
+    /**
+     * Returns a comma seperated header string from the keys of the column
+     * row map.
+     * 
+     * @param row -- column header row map
+     * @return -- comma seperated header string
+     */
+    private String createColumnHeader(Map<String, Object> row) {
+        return row.entrySet()
+                .stream()
+                .map(Map.Entry::getKey)
+                .collect(Collectors.joining(","));
+    }
 }
diff --git a/Core/src/org/sleuthkit/autopsy/sqlitereader/SQLiteReader.java b/Core/src/org/sleuthkit/autopsy/sqlitereader/SQLiteReader.java
new file mode 100755
index 0000000000000000000000000000000000000000..34cd2969e7c79b55d7ba7d5048f52ab96b656b3e
--- /dev/null
+++ b/Core/src/org/sleuthkit/autopsy/sqlitereader/SQLiteReader.java
@@ -0,0 +1,272 @@
+/*
+ * Autopsy Forensic Browser
+ *
+ * Copyright 2018-2018 Basis Technology Corp.
+ * Contact: carrier <at> sleuthkit <dot> org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.sleuthkit.autopsy.sqlitereader;
+
+import java.io.File;
+import java.io.IOException;
+import java.sql.Connection;
+import java.sql.DriverManager;
+import java.sql.ResultSet;
+import java.sql.ResultSetMetaData;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.TreeMap;
+import org.openide.util.NbBundle;
+import org.sleuthkit.autopsy.casemodule.Case;
+import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
+import org.sleuthkit.autopsy.casemodule.services.FileManager;
+import org.sleuthkit.autopsy.casemodule.services.Services;
+import org.sleuthkit.autopsy.datamodel.ContentUtils;
+import org.sleuthkit.datamodel.AbstractFile;
+import org.sleuthkit.datamodel.SleuthkitCase;
+import org.sleuthkit.datamodel.TskCoreException;
+
+/**
+ * Reads rows from SQLite tables and returns results in a list collection.
+ */
+public class SQLiteReader implements AutoCloseable {
+    
+    private final Connection connection;
+    
+    /**
+     * Writes data source file contents to local disk and opens a sqlite JDBC
+     * connection. 
+     * 
+     * @param sqliteDbFile Data source abstract file
+     * @param localDiskPath Location for database contents to be copied to
+     * @throws ClassNotFoundException missing SQLite JDBC class
+     * @throws SQLException Exception opening JDBC connection
+     * @throws IOException Exception writing file contents
+     * @throws NoCurrentCaseException Current case closed during file copying
+     * @throws TskCoreException Exception finding files from abstract file
+     */
+    public SQLiteReader(AbstractFile sqliteDbFile, String localDiskPath) throws ClassNotFoundException, 
+            SQLException, IOException, NoCurrentCaseException, TskCoreException{
+        
+        writeDataSourceToLocalDisk(sqliteDbFile, localDiskPath);
+        connection = getDatabaseConnection(localDiskPath);
+    }
+    
+    /**
+     * Copies the data source file contents to local drive for processing.
+     * 
+     * @param file AbstractFile from the data source 
+     * @param localDiskPath Local drive path to copy AbstractFile contents
+     * @throws IOException Exception writing file contents
+     * @throws NoCurrentCaseException Current case closed during file copying
+     * @throws TskCoreException Exception finding files from abstract file
+     */
+    private void writeDataSourceToLocalDisk(AbstractFile file, String localDiskPath) 
+        throws IOException, NoCurrentCaseException, TskCoreException {
+        
+        File localDatabaseFile = new File(localDiskPath);
+        if (!localDatabaseFile.exists()) {
+            ContentUtils.writeToFile(file, localDatabaseFile);
+
+            // Look for any meta files associated with this DB - WAL, SHM, etc. 
+            findAndCopySQLiteMetaFile(file, file.getName() + "-wal");
+            findAndCopySQLiteMetaFile(file, file.getName() + "-shm");
+        }
+    }
+    
+    /**
+     * Searches for a meta file associated with the give SQLite database. If found,
+     * copies the file to the local disk folder
+     * 
+     * @param sqliteFile SQLIte db file being processed
+     * @param metaFileName name of meta file to look for
+     * @throws NoCurrentCaseException Case has been closed.
+     * @throws TskCoreException fileManager cannot find AbstractFile files.
+     * @throws IOException Issue during writing to file.
+     */
+    private void findAndCopySQLiteMetaFile(AbstractFile sqliteFile,
+            String metaFileName) throws NoCurrentCaseException, TskCoreException, IOException {
+        
+        Case openCase = Case.getCurrentCaseThrows();
+        SleuthkitCase sleuthkitCase = openCase.getSleuthkitCase();
+        Services services = new Services(sleuthkitCase);
+        FileManager fileManager = services.getFileManager();
+        
+        List<AbstractFile> metaFiles = fileManager.findFiles(
+                sqliteFile.getDataSource(), metaFileName, 
+                sqliteFile.getParent().getName());
+        
+        if (metaFiles != null) {
+            for (AbstractFile metaFile : metaFiles) {
+                String tmpMetafilePathName = openCase.getTempDirectory() + 
+                        File.separator + metaFile.getName();
+                File tmpMetafile = new File(tmpMetafilePathName);
+                ContentUtils.writeToFile(metaFile, tmpMetafile);
+            }
+        }
+    }
+
+    /**
+     * Opens a JDBC connection to the sqlite database specified by the path
+     * parameter.
+     * 
+     * @param databasePath Local path of sqlite database
+     * @return Connection JDBC connection, to be maintained and closed by the reader
+     * @throws ClassNotFoundException missing SQLite JDBC class
+     * @throws SQLException Exception during opening database connection
+     */
+    private Connection getDatabaseConnection(String databasePath) 
+            throws ClassNotFoundException, SQLException {
+        
+        // Load the SQLite JDBC driver, if necessary.
+        Class.forName("org.sqlite.JDBC"); //NON-NLS  
+        return DriverManager.getConnection(
+                "jdbc:sqlite:" + databasePath); //NON-NLS
+    }
+    
+    
+    /**
+     * Retrieves a map view of table names to table schemas (in the form of
+     * CREATE TABLE statments).
+     * 
+     * @return A map of table names to table schemas
+     * @throws SQLException
+     */
+    public Map<String, String> getTableSchemas()
+            throws SQLException {
+        
+        Map<String, String> dbTablesMap = new TreeMap<>();
+        
+        try (Statement statement = connection.createStatement();
+                ResultSet resultSet = statement.executeQuery(
+                    "SELECT name, sql FROM sqlite_master " //NON-NLS
+                    + " WHERE type= 'table' " //NON-NLS
+                    + " ORDER BY name;")){ //NON-NLS
+            
+            while (resultSet.next()) {
+                String tableName = resultSet.getString("name"); //NON-NLS
+                String tableSQL = resultSet.getString("sql"); //NON-NLS
+                dbTablesMap.put(tableName, tableSQL);
+            }
+        }
+        
+        return dbTablesMap;
+    }
+    
+    /**
+     * Retrieves the total number of rows from a table in the SQLite database.
+     * 
+     * @param tableName
+     * @return Row count from tableName
+     * @throws SQLException
+     */
+    public Integer getTableRowCount(String tableName) throws SQLException {
+        try (Statement statement = connection.createStatement();
+                ResultSet resultSet = statement.executeQuery(
+                        "SELECT count (*) as count FROM " + tableName)){ //NON-NLS
+            return resultSet.getInt("count"); //NON-NLS
+        }
+    }
+    
+    /**
+     * Retrieves all rows from a given table in the SQLite database. If only a 
+     * subset of rows are desired, see the overloaded function below.
+     * 
+     * @param tableName
+     * @return List of rows, where each row is 
+     * represented as a column-value map.
+     * @throws SQLException
+     */
+    public List<Map<String, Object>> getRowsFromTable(String tableName) throws SQLException {
+        
+        //This method does not directly call its overloaded counterpart 
+        //since the second parameter would need to be retreived from a call to
+        //getTableRowCount().
+        try(Statement statement = connection.createStatement();
+                ResultSet resultSet = statement.executeQuery(
+                        "SELECT * FROM " + tableName)) { //NON-NLS
+            return resultSetToList(resultSet);
+        }
+    }
+    
+    /**
+     * Retrieves a subset of the rows from a given table in the SQLite database.
+     * 
+     * @param tableName
+     * @param startRow Desired start index (rows begin at 1)
+     * @param numRowsToRead Number of rows past the start index
+     * @return List of rows, where each row is 
+     * represented as a column-value map.
+     * @throws SQLException
+     */
+    public List<Map<String, Object>> getRowsFromTable(String tableName, 
+            int startRow, int numRowsToRead) throws SQLException{
+        
+        try(Statement statement = connection.createStatement();
+                ResultSet resultSet = statement.executeQuery(
+                        "SELECT * FROM " + tableName    //NON-NLS
+                        + " LIMIT " + Integer.toString(numRowsToRead) //NON-NLS
+                        + " OFFSET " + Integer.toString(startRow - 1))) { //NON-NLS
+            return resultSetToList(resultSet);
+        }
+    }
+    
+    /**
+     * Converts a ResultSet (row results from a table read) into a list. 
+     * 
+     * @param resultSet row results from a table read
+     * @return List of rows, where each row is 
+     * represented as a column-value map.
+     * @throws SQLException occurs if ResultSet is closed while attempting to 
+     * access it's data.
+     */
+    @NbBundle.Messages("SQLiteReader.BlobNotShown.message=BLOB Data not shown")
+    private List<Map<String, Object>> resultSetToList(ResultSet resultSet) throws SQLException {
+        
+        ResultSetMetaData metaData = resultSet.getMetaData();
+        int columns = metaData.getColumnCount();
+        List<Map<String, Object>> rowMap = new ArrayList<>();
+        while (resultSet.next()) {
+            Map<String, Object> row = new LinkedHashMap<>(columns);
+            for (int i = 1; i <= columns; ++i) {
+                if (resultSet.getObject(i) == null) {
+                    row.put(metaData.getColumnName(i), "");
+                } else {
+                    if (metaData.getColumnTypeName(i).compareToIgnoreCase("blob") == 0) {
+                        row.put(metaData.getColumnName(i), Bundle.SQLiteReader_BlobNotShown_message());
+                    } else {
+                        row.put(metaData.getColumnName(i), resultSet.getObject(i));
+                    }
+                }
+            }
+            rowMap.add(row);
+        }
+
+        return rowMap;
+    }
+
+    /**
+     * Closes underlying JDBC connection.
+     * 
+     * @throws SQLException 
+     */
+    @Override
+    public void close() throws SQLException {
+        connection.close();
+    }
+}