Quiero almacenar un video en la base de datos sqlite. PD No quiero almacenar la ruta, sino el contenido real del video.
A menos que los videos sean muy cortos y ocupen poco espacio (por ejemplo, hasta 200 000 cada uno, tal vez 1/10 de segundo, pero dependerá del formato en el que se guarden), es probable que encuentre problemas y excepciones/bloqueos.
- Usar un teléfono alrededor de 2 segundos de negro tomó 2.2Mb, 2 segundos de grabar un video tomó 7Mb.
Aunque SQLite tiene la capacidad de almacenar BLOB relativamente grandes según:-
Longitud máxima de una cadena o BLOB
El número máximo de bytes en una cadena o BLOB en SQLite está definido por la macro del preprocesador SQLITE_MAX_LENGTH. El valor predeterminado de esta macro es 1000 millones (1000 millones o 1 000 000 000). Puede aumentar o disminuir este valor en tiempo de compilación mediante una opción de línea de comandos como esta:
-DSQLITE_MAX_LENGTH=123456789 La implementación actual solo admitirá una longitud de cadena o BLOB de hasta 231-1 o 2147483647. Y algunas funciones integradas, como hex(), podrían fallar mucho antes de ese punto. En aplicaciones sensibles a la inseguridad, es mejor no intentar aumentar la longitud máxima de cadenas y blobs. De hecho, podría hacer bien en reducir la longitud máxima de cadena y blob a algo más en el rango de unos pocos millones si eso es posible.
Durante parte del procesamiento INSERT y SELECT de SQLite, el contenido completo de cada fila en la base de datos se codifica como un solo BLOB. Entonces, el parámetro SQLITE_MAX_LENGTH también determina el número máximo de bytes en una fila.
La longitud máxima de cadena o BLOB se puede reducir en tiempo de ejecución mediante la interfaz sqlite3_limit(db,SQLITE_LIMIT_LENGTH,size). Límites en SQLite
CursorWindow del SDK de Android tiene una limitación de 2Mb y eso es para todas las columnas de la(s) fila(s) de los búferes. Como tal, incluso si puede almacenar videos con éxito, es posible que no pueda recuperar esos videos.
La forma recomendada es lo que no desea, es decir, almacenar la ruta al Video.
Si almaceno el video en mi almacenamiento interno/externo y almaceno la ruta en su lugar, ¿cómo podré acceder al mismo desde otro dispositivo?
Tendrías el mismo problema con la base de datos ya que normalmente se almacena dentro de los datos de aplicaciones que están protegidos. Eso es a menos que la base de datos sea una base de datos preexistente (es decir, llena de datos), en cuyo caso la base de datos se distribuye con la aplicación a través del APK.
Si se trata de una base de datos preexistente distribuida a través del APK, los videos también se pueden distribuir como parte del APK y, por lo tanto, tan protegidos y expuestos como la base de datos.
Si su intención es distribuir videos entre dispositivos que no forman parte del APK, entonces SQlite probablemente no sea la solución correcta, ya que es una base de datos integrada y no tiene funcionalidad de cliente/servidor incorporada.
Además, si mi dispositivo se formatea, perderé todos los datos.
En tal escenario, la base de datos sería tan vulnerable como cualquier otro dato , ya que eso es todo lo que es la base de datos, un archivo, como un video, un documento de Word, etc., que necesitan una aplicación adecuada para ver/cambiar el contenido. Sin embargo, si la base de datos es una base de datos preexistente, la simple reinstalación de la aplicación restauraría la base de datos y otros archivos del APK.
Ejemplo de trabajo
Esto utiliza el método Sugerido/Recomendado asumiendo que los videos se distribuirán con el APK.
- Videos de notas cortesía de Videos de muestra
Después de crear un nuevo proyecto, se descargaron 4 videos y se copiaron en la carpeta res/raw (después de crear la carpeta raw) según:-
El ayudante de base de datos (subclase de SQLiteOpenHelper) se creó para una tabla de 2 columnas y con _id columna (nota llamada _id para usar con SimpleCursorAdapter ).- video_path para almacenar la ruta/nombre del video (no la ruta completa pero suficiente para poder determinar la ruta a partir de los datos almacenados)- Nota ÚNICA se ha codificado para evitar que se agreguen duplicados.
Con algún método básico para permitir que se agreguen y eliminen filas y para que se extraigan todas las filas (a través de un Cursor para usar con SimpleCursorAdapter).
DBHelper.java
public class DBHelper extends SQLiteOpenHelper {
public static final String DBNAME = "myvideos";
public static final int DBVERSION = 1;
public static final String TBL_VIDEO = "video";
public static final String COL_VIDEO_ID = BaseColumns._ID;
public static final String COL_VIDEO_PATH = "video_path";
SQLiteDatabase mDB;
public DBHelper(Context context) {
super(context, DBNAME, null, DBVERSION);
mDB = this.getWritableDatabase();
}
@Override
public void onCreate(SQLiteDatabase db) {
String crt_video_table = "CREATE TABLE IF NOT EXISTS " + TBL_VIDEO + "(" +
COL_VIDEO_ID + " INTEGER PRIMARY KEY," +
COL_VIDEO_PATH + " TEXT UNIQUE" +
")";
db.execSQL(crt_video_table);
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
}
public long addVideo(String path) {
ContentValues cv = new ContentValues();
cv.put(COL_VIDEO_PATH,path);
return mDB.insert(TBL_VIDEO,null,cv);
}
public Cursor getVideos() {
return mDB.query(TBL_VIDEO,null,null,null,null,null,null);
}
public int deleteVideoFromDB(long id) {
String whereclause = COL_VIDEO_ID + "=?";
String[] whereargs = new String[]{String.valueOf(id)};
return mDB.delete(TBL_VIDEO,whereclause,whereargs);
}
}
Un MainActivity.java bastante sencillo (ver comentarios)
public class MainActivity extends AppCompatActivity {
TextView mMyTextView;
ListView mVideoList;
VideoView mVideoViewer;
DBHelper mDBHlpr;
Cursor mCsr;
SimpleCursorAdapter mSCA;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mMyTextView = this.findViewById(R.id.mytext);
mVideoList = this.findViewById(R.id.videolist);
mVideoViewer = this.findViewById(R.id.videoviewer);
mDBHlpr = new DBHelper(this);
addVideosFromRawResourceToDB();
}
@Override
protected void onDestroy() {
mCsr.close(); //<<<<<<<<<< clear up the Cursor
super.onDestroy();
}
@Override
protected void onResume() {
super.onResume();
manageListView(); //<<<<<<<<<< rebuild and redisplay the List of Videos (in case they have changed)
}
/**
* Setup or Refresh the ListView adding the OnItemClick and OnItemLongClick listeners
*/
private void manageListView() {
mCsr = mDBHlpr.getVideos();
// Not setup so set it up
if (mSCA == null) {
// Instantiate the SimpleCursorAdapter
mSCA = new SimpleCursorAdapter(
this,
android.R.layout.simple_list_item_1, // Use stock layout
mCsr, // The Cursor with the list of videos
new String[]{DBHelper.COL_VIDEO_PATH}, // the column (columns)
new int[]{android.R.id.text1}, // the view id(s) into which the column(s) data will be placed
0
);
mVideoList.setAdapter(mSCA); // Set the adpater for the ListView
/**
* Add The Long Click Listener (will delete the video row from the DB (NOT the video))
*/
mVideoList.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() {
@Override
public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) {
mDBHlpr.deleteVideoFromDB(id);
manageListView(); // <<<<<<<<<< refresh the ListView as data has changed
return true;
}
});
/**
* Play the respective video when the item is clicked
* Note Cursor should be at the correct position so data can be extracted directly from the Cursor
*/
mVideoList.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
setCurrentVideo(mCsr.getString(mCsr.getColumnIndex(DBHelper.COL_VIDEO_PATH)));
}
});
} else {
mSCA.swapCursor(mCsr); //<<<<<<<<<< apply the changed Cursor
}
}
/**
* Set the currrent video and play it
* @param path the path (resource name of the video)
*/
private void setCurrentVideo(String path) {
mVideoViewer.setVideoURI(
Uri.parse(
"android.resource://" + getPackageName() + "/" + String.valueOf(
getResources().getIdentifier(
path,
"raw",
getPackageName())
)
)
);
mVideoViewer.start();
}
/**
* Look at all the resources in the res/raw folder and add the to the DB (not if they are duplicates due to UNQIUE)
*/
private void addVideosFromRawResourceToDB() {
Field[] fields=R.raw.class.getFields();
for(int count=0; count < fields.length; count++){
Log.i("Raw Asset: ", fields[count].getName());
mDBHlpr.addVideo(fields[count].getName());
}
}
}
Resultados
Cuando empezó (no se reproduce nada) :-
Después de mucho hacer clic en el video de 1Mb (borrando la entrada de la base de datos):-
Después de hacer clic en un video en la lista:-