SQLiteBlobTooBigException:写入数据库时​​行太大而无法放入 CursorWindow

Posted

技术标签:

【中文标题】SQLiteBlobTooBigException:写入数据库时​​行太大而无法放入 CursorWindow【英文标题】:SQLiteBlobTooBigException: Row too big to fit into CursorWindow while writing to DB 【发布时间】:2019-11-28 03:43:22 【问题描述】:

当我尝试添加到 android 上的 sqllite DB 时,我看到了上述错误。我正在使用 cloudant 库。这是堆栈跟踪:

2019-07-17 18:04:48.292 5522-5753/org.iprd.identity E/SQLiteQuery: exception: Row too big to fit into CursorWindow requiredPos=0, totalRows=1; query: SELECT docs.docid, docs.doc_id, revid, sequence, current, deleted, parent, json FROM revs, docs WHERE docs.docid=? AND revs.doc_id=docs.doc_id AND revid=? ORDER BY revs.sequence LIMIT 1
2019-07-17 18:04:48.302 5522-5522/org.iprd.identity E/DatabaseImpl: Failed to create document
    java.util.concurrent.ExecutionException: android.database.sqlite.SQLiteBlobTooBigException: Row too big to fit into CursorWindow requiredPos=0, totalRows=1
        at java.util.concurrent.FutureTask.report(FutureTask.java:123)
        at java.util.concurrent.FutureTask.get(FutureTask.java:193)
        at com.cloudant.sync.internal.documentstore.DatabaseImpl.get(DatabaseImpl.java:1084)
        at com.cloudant.sync.internal.documentstore.DatabaseImpl.create(DatabaseImpl.java:925)

我以前从未遇到过这个问题,但是,我最近进行了更改并开始使用 3rd 方工具给我一些图像。使用这个新工具,图像尺寸似乎比以前大得多。我通过对图像进行 Base64 编码将这些图像作为字符串存储在我的数据库中。但是,使用这个新工具,在存储图像时,我得到了上述异常。

我有一个单元测试,我尝试使用该图像中生成的字符串之一,但这也会引发错误,即字符串大小太大,甚至无法编译。

解决此问题的最佳方法是什么? -

    我想做的是将图像存储在设备本身中,并将路径存储在数据库中。

    我们可以对图像进行一些压缩以减小其文件大小吗?

    是否可以调整一些 db 设置以确保它可以处理更大的图像尺寸?

提前致谢!

【问题讨论】:

将数据存储为二进制 blob 而不是 base-64 编码字符串将节省空间。但是 iirc 对于 blob 或字符串的大小,Android 版本的 sqlite 有一个相当小的上限。 【参考方案1】:

我想做的是将图像存储在设备中 本身,只需将路径存储在数据库中。

不打算回答,因为你最适合回答这个问题。

我们可以对图像进行一些压缩以减少其文件吗 尺寸?

可能,但这并不一定会消除问题。即使您将图像压缩为 2Mb(光标窗口的 4Mb 限制的一半),那么可能一次只有 1 行适合光标窗口。结果可能是性能很差,并且瓶头不是真正的 SQLite,而是处理光标。

不推荐

另一种解决方案,但仍然很昂贵,是将图像存储在部分/块中,可以在here 找到这样做的示例(注意没有进行调整来建立 256kb 块大小)。 注意到响应时间被认为很差

不推荐,但如果性能不佳不是问题,那么可能是一个解决方案

不需要额外压缩/解压缩开销的解决方案是将图像或超过设定大小的图像存储在文件系统(例如外部存储)中,并存储路径或路径的一部分。

后一种选择,将小图像存储在数据库中,将大图像存储为文件,可以利用35% Faster Than The Filesystem)。

answer here 中的混合路径/blob 解决方案示例。

注意此示例可用作仅存储路径或混合解决方案的基础。

推荐

是否可以调整一些数据库设置以确保它可以处理 更大的图像尺寸?

是的。

你可以考虑:-

优化您的查询。作为开始,请参阅 EXPLAIN QUERY PLAN 和 The SQLite Query Optimizer Overview 增加页面大小(注意限制), 增加cache size, 冒险将synchronous 关闭并 冒险使用exclusive locking。 应用compile time options 来编译您自己调整过的SQLite 版本。 进一步阅读上述链接,因为可能有其他适用的调整设置。

【讨论】:

【参考方案2】:

我的解决方法如下:当我从远程银行搜索图像时,我会检查它们的大小,如果它们大于 500 kb,我会进行大小调整。

produto.setImagem_img(imagemTratada(rs.getBytes("imagem_img")));

施展魔法的方法:

private byte[] imagemTratada(byte[] imagem_img)

    while (imagem_img.length > 500000)
        Bitmap bitmap = BitmapFactory.decodeByteArray(imagem_img, 0, imagem_img.length);
        Bitmap resized = Bitmap.createScaledBitmap(bitmap, (int)(bitmap.getWidth()*0.8), (int)(bitmap.getHeight()*0.8), true);
        ByteArrayOutputStream stream = new ByteArrayOutputStream();
        resized.compress(Bitmap.CompressFormat.PNG, 100, stream);
        imagem_img = stream.toByteArray();
    
    return imagem_img;


【讨论】:

【参考方案3】:

是的 - 这是一个真正的问题。但我为此实施了修复。我们基本上是在我们的文档中的生物特征图像。我现在做什么:将各个生物特征实体(例如:指纹)存储在单独的文档中。拥有一个具有此文档 ID 的主文档。

例如:我们为一个人捕捉拇指和食指。我们存储 2 个文档来存储它们 - 我们称它们为 thumb.json 和 index.json。它们的文档 ID 分别为 thumb_100 和 index_100。现在,我们有一个主生物识别文档,看起来像这样:

“拇指”:“拇指_100” "index": "index_100"

这有助于我们从主文档中获取拇指和食指指纹。更重要的是,它确保单个行大小不会超出限制或导致如前所述的异常。

【讨论】:

【参考方案4】:

我发现如何使用 length() 和 substr() 只请求 1MB(CursorWindow 的最大值为 2MB),也许它会help。 (以防用户遇到此问题并且您需要避免异常)此外,您可以将context.getContentResolver().query() 与仅包含相关列的选择一起使用,这样您就不会加载与其他列相关的数据(并保存一些空间)。否则,我会推荐您建议的第一个选项,即将文件存储在内部存储中,并将文件路径存储在数据库中。

【讨论】:

【参考方案5】:

如果要将图像保存为 SQLite 或 Room 中的字节数组,则应压缩字节数组。

喜欢这个

Bitmap bmp = intent.getExtras().get("data");
ByteArrayOutputStream stream = new ByteArrayOutputStream();
bmp.compress(Bitmap.CompressFormat.PNG, 100, stream);
byte[] byteArray = stream.toByteArray();

【讨论】:

好吧,这行不通,因为Bitmap.CompressFormat.PNG 忽略了quality - link【参考方案6】:

将图像存储在您的应用程序私有存储下的设备中。并将该图像路径存储在数据库中。

【讨论】:

以上是关于SQLiteBlobTooBigException:写入数据库时​​行太大而无法放入 CursorWindow的主要内容,如果未能解决你的问题,请参考以下文章