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.