Home > Mobile >  Why can't my phone app open a file it stored in a Room Database?
Why can't my phone app open a file it stored in a Room Database?

Time:11-17

Problem: My Android phone app can open various file types stored in an Android Room pre-populated SQLite database but it cannot open files the app itself has added to the pre-populated database (except it can open .txt files). I believe the issue is probably with how the I coded the copying and conversion of a selected file to byte[] data. The app is java based, and I have done this in Java before in a desktop app, so I just can't seem to find the issue. Maybe it is a permission issue, I'm just not sure and someone standing outside looking in may see what I can't.

What I have tried: Since the app can open various existing pre-populated files successfully from the DB, I've concentrated on and stepped through methods writing files to the DB. I'm not receiving any errors. I suspect it may just be minor issue since I can't seem to see it.

What I'm trying to do: I'm trying to emulate the desktop version of this app into a Android phone version. I know it's not recommended or common practice to populate files to a DB, but this app needs to be able to read and write files to the DB supporting it. This will be a full range of file types like the desktop version (e.g., pics, docs, audio, video, etc.). However, as I stated above, .txt files seem to have no issue. The user can select files stored on their phone into a table that captures the fileName and filePath to a TableRow in a TableLayout. Below are methods involved. The plan is to refactor functionality once I get it working:

Capturing the full path and filename for each row - Uses the captured filepath to convert to a byte[] to store the data. The filename and file byte data are stored in a Files table, example, Files(fileName, fileData(byte[])). Each file is added to an ArrayList<Files> which the method returns

public static List<Files> captureNoteFiles(TableLayout table){
    List<Files> noteFiles = new ArrayList<>();
    int i = table.getChildCount();
    if(i>1){
        for (int itr = 1; itr<i; itr  ) { // iterating through indexes
            TableRow tr = (TableRow) table.getChildAt(itr);
            TextView tv = (TextView) tr.getChildAt(1); // 1 is the file path position
            File f = new File(tv.getText().toString());
            String n = f.getName();

            try {
                FileInputStream fis = new FileInputStream(f.getPath());
                ByteArrayOutputStream bos = new ByteArrayOutputStream();
                byte[] buf = new byte[1024];
                for (int read; (read = fis.read(buf)) != -1; ) {
                    bos.write(buf, 0, read);
                }
                fis.close();

                noteFiles.add(new Files(0, n, bos.toByteArray()));
            } catch (Exception e) {
                e.printStackTrace();
                Log.d("Input File", e.toString());
            }
        }
    }
    return noteFiles;
}

Iteration of the ArrayList - The ArrayList<Files> is iterated and populated to the Files table and an ID capture to associate those files with a particular note of reference.

public static void addNewNoteFiles(int noteID, List<Files> nf){
    if(nf.size()>0) {
        for (Files f : nf) {
            long id = rdb.getFilesDao().addFile(f);
            rdb.getFilesByNoteDao().insert(new FilesByNote(noteID, (int) id));
        }
    }
}

Files Entity

@Entity(tableName = "Files")
public class Files implements Parcelable {

    @PrimaryKey(autoGenerate = true)
    @ColumnInfo(name = "FileID")
    private int fileID;

    @ColumnInfo(name = "FileName")
    private String fileName;

    @TypeConverters(FileTypeConverter.class)
    @ColumnInfo(name = "FileData", typeAffinity = ColumnInfo.TEXT)
    private byte[] fileData;

    @SuppressWarnings(RoomWarnings.CURSOR_MISMATCH)
    public Files(int fileID, String fileName, byte[] fileData){
        this.fileID = fileID;
        this.fileName = fileName;
        this.fileData = fileData;
    }
}

CodePudding user response:

First you are assuming that an insert works as per :-

        long id = rdb.getFilesDao().addFile(f);
        rdb.getFilesByNoteDao().insert(new FilesByNote(noteID, (int) id));

What if the row isn't inserted? and returns an id of -1?

So I'd suggest adding getters to the Files class such as :-

public int getFileID() {
    return fileID;
}

public String getFileName() {
    return fileName;
}

public byte[] getFileData() {
    return fileData;
}

and then add the following to FilesDao :-

@Query("SELECT coalesce(length(FileData)) FROM Files WHERE FileID=:fileId")
abstract long getFilesDataLength(long fileId);

and then amending the addNewNoteFiles to be :-

public static void addNewNoteFiles(int noteID, List<Files> nf){

    final String TAG = "ADDNEWNOTE";
    if(nf.size()>0) {
        for (Files f : nf) {
            long id = rdb.getFilesDao().addFile(f);
            if (id > 0) {
                long lengthOfFileData = rdb.getFilesDao().getFilesDataLength(id);
                Log.d(TAG,
                        "Inserted File = "   f.getFileName()  
                                " DataLength = "   f.getFileData().length  
                                " ID = "   f.getFileID()  
                                " Length of Stored Data = "   lengthOfFileData);
                if (f.getFileData().length != lengthOfFileData) {
                    Log.d(TAG,"WARNING FileData length MISMATCH for File = "   f.getFileName()   "\n\t Expected "   f.getFileData().length   " Found "   lengthOfFileData);
                }
                rdb.getFilesByNoteDao().insert(new FilesByNote(noteID, (int) id));

            } else {
                Log.d(TAG,"NOT INSERTED File = "   f.getFileName());
            }
        }
    }
}

Run and check the log. Are all the files inserted? Do the lengths match? Are the lengths as expected (if all 0 lengths, or some, then obviously something is amiss when building the ByteArrayOutputStream)

You may wish to add similar for inserting the FilesByNote i.e. have the insert Dao return a long (it returns the rowid) and check if the value is > 0.

  • You may wonder what rowid is. Well it's a normally hidden column, perhaps hidden as it would appear that FilesByNotes is an associative table mapping(associating) Note(s) with Files and as such has a composite primary key NoteId and FileId which is not an alias of the rowid, so rowid will be hidden as such. However, the value will be auto-generated or -1 if no row is inserted.
    • ALL tables, with the exception of tables defined with WITHOUT ROWID, have a rowid column. Room does not allow thee definition of WITHOUT ROWID tables.

You wouldn't be concerned about the value if it's greater than 0, just that it is greater than 0 and thus a row was inserted.

The above may help to determine any issues encountered when inserting the data. If there are none found then the issue is else where.

  • Related