SQLite
 sql >> Base de Dados >  >> RDS >> SQLite

Como armazenar conteúdo de vídeo no banco de dados SQLite (não no caminho do vídeo)


Eu quero armazenar um vídeo no banco de dados sqlite. P.S. Eu não quero armazenar o caminho, mas o conteúdo real do vídeo.

A menos que os vídeos sejam muito curtos e ocupem pouco espaço (digamos até 200k cada, talvez 1/10 de segundo, mas depende do formato em que são salvos), você provavelmente encontrará problemas e exceções/travamentos.
  • Usar um telefone em torno de 2 segundos de preto consumia 2,2 Mb, enquanto 2 segundos de gravação de vídeo consumia 7 Mb.

Embora o SQLite tenha a capacidade de armazenar BLOBs relativamente grandes conforme:-

  • Comprimento máximo de uma string ou BLOB

    O número máximo de bytes em uma string ou BLOB no SQLite é definido pela macro do pré-processador SQLITE_MAX_LENGTH. O valor padrão dessa macro é 1 bilhão (1 bilhão ou 1.000.000.000). Você pode aumentar ou diminuir esse valor em tempo de compilação usando uma opção de linha de comando como esta:

    -DSQLITE_MAX_LENGTH=123456789 A implementação atual suportará apenas uma string ou comprimento BLOB até 231-1 ou 2147483647. E algumas funções internas como hex() podem falhar bem antes desse ponto. Aplicativos sensíveis à insegurança, é melhor não tentar aumentar a string máxima e o comprimento do blob. Na verdade, você pode fazer bem em diminuir o comprimento máximo da string e do blob para algo mais na faixa de alguns milhões, se isso for possível.

    Durante parte do processamento INSERT e SELECT do SQLite, o conteúdo completo de cada linha no banco de dados é codificado como um único BLOB. Assim, o parâmetro SQLITE_MAX_LENGTH também determina o número máximo de bytes em uma linha.

    O comprimento máximo de string ou BLOB pode ser reduzido em tempo de execução usando a interface sqlite3_limit(db,SQLITE_LIMIT_LENGTH,size).Limites no SQLite

A CursorWindow do SDK do Android tem uma limitação de 2Mb e isso é para todas as colunas da(s) linha(s) se buffers. Assim, mesmo que você consiga armazenar vídeos com sucesso, talvez não consiga recuperar esses vídeos.

A maneira recomendada é o que você não deseja, ou seja, armazenar o caminho para o vídeo.

Se eu armazenar o vídeo no meu armazenamento interno/externo e armazenar o caminho, como poderei acessar o mesmo de outro dispositivo.

Você teria o mesmo problema com o banco de dados como normalmente é armazenado nos dados de aplicativos que são protegidos. Ou seja, a menos que o banco de dados seja um banco de dados pré-existente (ou seja, preenchido com dados), caso em que o banco de dados é distribuído com o aplicativo por meio do APK.

Se este último, um banco de dados pré-existente distribuído através do APK, os vídeos também podem ser distribuídos como parte do APK e, portanto, tão protegidos e exponíveis quanto o banco de dados.

Se sua intenção é distribuir vídeos entre dispositivos que não fazem parte do APK, o SQlite provavelmente não é a solução correta, pois é um banco de dados incorporado e não possui funcionalidade cliente/servidor incorporada.

Além disso, se meu dispositivo for formatado, perderei todos os dados.

Nesse cenário, o banco de dados seria tão vulnerável quanto qualquer outro dado , pois isso é tudo o que o banco de dados é, um arquivo, assim como um vídeo, um documento do Word etc., todos precisam de um aplicativo adequado para visualizar/alterar o conteúdo. No entanto, se o banco de dados for um banco de dados pré-existente, basta reinstalar o aplicativo para restaurar o banco de dados e outros arquivos do APK.

Exemplo de trabalho

Isso usa o método sugerido/recomendado, supondo que os vídeos sejam distribuídos com o APK.

  • Observar vídeos cortesia de vídeos de amostra

Após a criação do novo projeto, 4 vídeos foram baixados e copiados para a pasta res/raw (depois de criar a pasta raw) conforme :-



O Database Helper (subclasse de SQLiteOpenHelper) foi criado para uma tabela de 2 colunas com- _id coluna (nota chamada _id para uso com SimpleCursorAdapter ).- video_path para armazenar o caminho/nome do vídeo (não o caminho completo, mas suficiente para poder determinar o caminho dos dados armazenados)- Nota UNIQUE foi codificado para impedir que duplicatas sejam adicionadas.

Com algum método básico para permitir que linhas sejam adicionadas e excluídas e que todas as linhas sejam extraídas (por meio de um Cursor para uso com o 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);
    }
}

Um MainActivity.java bastante simples (Ver comentários)
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

Quando começou (nada é reproduzido):-



Depois de um longo clique no vídeo de 1Mb (excluindo a entrada do banco de dados):-



Depois de clicar em um vídeo na lista:-