【问题标题】:How to store video content in SQLite database (not the video path)如何在 SQLite 数据库中存储视频内容(不是视频路径)
【发布时间】:2019-02-15 10:27:57
【问题描述】:

我想在 sqlite 数据库中存储一个视频。附言我不想存储路径,而是存储实际的视频内容。 我已将视频转换为字节数组并将字节数组存储在 sqlite 数据库中。检索 bytearray 后,将被转换为 File。但是视频没有播放。请帮忙。

【问题讨论】:

  • 这绝对是错误的。您无法将其保存在数据库中。如果添加 2 3 个条目,那么之后您将无法在 db 中查询。你为什么要这个?
  • 也许我可以给你一个很好的答案
  • 那应该是什么方法?如果我将视频存储在我的内部/外部存储中并存储路径,那么我将如何从其他设备访问相同的内容。此外,如果我的设备被格式化,那么我将丢失所有数据。
  • 如何附加我的代码?当我复制粘贴代码时,它不会让我粘贴相同的代码,因为它会标记许多错误。
  • SQLite 游标的大小限制为 1MB。因此,您无法在 SQLite 中存储视频。

标签: android android-sqlite android-database


【解决方案1】:

我想在 sqlite 数据库中存储一个视频。附言我不想 存储路径,但实际的视频内容。

除非视频非常短且占用空间很小(比如每个视频最多 200k,可能是 1/10 秒,但取决于保存的格式),否则您可能会遇到问题和异常/崩溃。

  • 使用手机大约 2 秒的黑色占用 2.2Mb,实际录制视频 2 秒占用 7Mb。

虽然 SQLite 有能力存储相对较大的 BLOB,但 :-

  • 字符串或BLOB的最大长度

    在 SQLite 中定义了字符串或 BLOB 中的最大字节数 由预处理器宏 SQLITE_MAX_LENGTH。这个的默认值 宏是 10 亿(1 亿或 1,000,000,000)。你可以 在编译时使用命令行选项提高或降低此值 像这样:

    -DSQLITE_MAX_LENGTH=123456789 当前的实现只支持长度为 231-1 或 2147483647 的字符串或 BLOB。还有一些 诸如 hex() 之类的内置函数可能在此之前就失败了。在 对安全敏感的应用程序最好不要尝试增加 最大字符串和 blob 长度。事实上,你最好降低 最大字符串和 blob 长度在 a 的范围内 如果可能的话,几百万。

    在 SQLite 的部分 INSERT 和 SELECT 处理过程中,完整的 数据库中每一行的内容被编码为单个 BLOB。所以 SQLITE_MAX_LENGTH 参数还决定了最大数量 连续字节。

    最大字符串或 BLOB 长度可以在运行时使用 sqlite3_limit(db,SQLITE_LIMIT_LENGTH,size) 接口。 Limits In SQLite

Android SDK 的 CursorWindow 有 2Mb 的限制,如果有缓冲区,则适用于行的所有列。因此,即使您可以成功存储视频,也可能无法检索这些视频。

推荐的方式是你不想要的,即存储视频的路径。

如果我将视频存储在内部/外部存储中并存储 路径而不是我将如何能够从某些人那里访问相同的路径 其他设备。

您会遇到与数据库相同的问题,因为它通常存储在受保护的应用程序数据中。除非数据库是预先存在的数据库(即填充了数据),在这种情况下,数据库是通过 APK 与应用程序一起分发的。

如果后者是通过 APK 分发的预先存在的数据库,那么视频也可以作为 APK 的一部分分发,因此与数据库一样受到保护和公开。

如果您打算在不属于 APK 的设备之间分发视频,那么 SQlite 可能不是正确的解决方案,因为它是一个嵌入式数据库并且没有内置客户端/服务器功能。

如果我的设备被格式化,那么我将丢失所有 数据。

在这种情况下,数据库与任何其他数据一样容易受到攻击,因为这就是数据库的全部内容,一个文件,就像一个视频,一个 word 文档等都需要一个合适的应用程序来查看/更改内容。但是,如果数据库是预先存在的数据库,则只需重新安装应用程序即可从 APK 中恢复数据库和其他文件。

工作示例

这使用建议/推荐方法,假设视频将与 APK 一起分发。

在创建新项目后,4 个视频被下载并复制到 res/raw 文件夹(创建 raw 文件夹后):-

数据库助手(SQLiteOpenHelper 的子类)是为 2 列表创建的 - _id 列(注释名为 _id 用于 SimpleCursorAdapter)。 - video_path 用于存储视频的路径/名称(不是完整路径,但足以能够从存储的数据中确定路径) - 注意 UNIQUE 已被编码以停止添加重复项。

使用一些基本方法来允许添加和删除行以及提取所有行(通过用于 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);
    }
}

一个非常简单的 MainActivity.java(参见 cmets)

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());
            }
    }
}

结果

刚开始时(没有播放):-

长按1Mb视频后(删除DB条目):-

点击列表中的视频后:-

【讨论】:

    【解决方案2】:

    你可以使用这种方法

    保存视频时,将其保存在应用程序私有存储文件夹中。

    Context.getFilesDir()
    

    这将为您提供..\Andorid\data\data\com.example.app 中的应用存储路径 它将在内部存储中。

    com.example.app 将是您的应用程序包 ID。您可以在此处创建一个新文件夹,例如Videos,然后将视频保存在此文件夹中。将其路径保存在数据库中。只有您的应用可以访问此文件夹。没有任何其他应用程序或设备用户可以访问此文件夹。因此,除了您的应用程序之外,没有人可以编辑或删除您的文件。

    此外,如果用户重置手机,这些数据以及您的数据库以及在某些情况下可能还会删除您的应用程序都会被删除。所以不用担心你的文件会被删除,但数据库仍然有它们的路径。如果文件被删除,那么 DB 也会被删除,但仅限于应用程序 Uninstall, device reset 或 SD card erase。

    【讨论】:

    • 将通过我的应用程序捕获的视频上传到 sql server 的代码?我们必须将其转换为字节数组并在服务器上上传相同的 uisng Volley 或者应该是什么方法?
    • 您可以很容易地在网上找到如何将视频转换为字节数组的代码。每当您想将视频上传到服务器时,就在发送之前进行转换
    猜你喜欢
    • 1970-01-01
    • 2019-08-30
    • 2016-10-20
    • 1970-01-01
    • 1970-01-01
    • 2018-05-17
    • 1970-01-01
    • 1970-01-01
    • 2016-04-20
    相关资源
    最近更新 更多