SQLite Zipfile 模块

、概述

zipfile 模块提供对简单ZIP 档案的读/写访问 当前的实现有以下限制:

  • 不支持加密。
  • 不支持跨多个文件的 ZIP 存档。
  • 不支持 zip64 扩展。
  • 唯一支持的压缩算法是 "deflate"

将来可能会取消部分或全部这些限制。

2.获取并编译zipfile

zipfile 模块的代码可以在 主 SQLite 源代码树的ext/misc/zipfile.c 文件中 找到可以使用如下命令将其编译成 SQLite 可加载扩展

gcc -g -fPIC -shared zipfile.c -o zipfile.so

或者,可以将 zipfile.c 文件编译到应用程序中。在这种情况下,应该调用以下函数来为每个新的数据库连接注册扩展:

int sqlite3_zipfile_init(sqlite3 *db, void*, void*);

传递的第一个参数应该是用于注册扩展的数据库句柄。第二个和第三个参数都应传递 0。

Zipfile 包含在命令行 shell的大多数版本中。

3.使用压缩文件

zipfile 模块提供了三个类似的接口来访问、更新和创建 zip 文件存档:

  1. 一个表值函数,它提供对文件系统或内存中现有档案的只读访问。
  2. 一个虚拟表,它提供对存储在文件系统中的档案的读写访问。
  3. 一个 SQL 聚合函数,可用于在内存中创建新存档。

zipfile 模块提供了两个类似的接口来访问 zip 档案。一个表值函数,它提供对现有档案的只读访问,以及一个虚拟表接口,它提供读写访问。

3.1. 表值函数(只读访问)

为了读取现有的 zip 存档,Zipfile 模块提供了一个 接受单个参数的表值函数。如果参数是文本值,那么它是从文件系统读取的 zip 存档的路径。或者,如果参数是 SQL blob,那么它就是 zip 存档数据本身。

例如,要从当前目录检查 zip 存档“test.zip”的内容:

SELECT * FROM zipfile('test.zip');

或者,从 SQLite shell 工具(readfile() 函数从文件系统读取文件的内容并将其作为 blob 返回):

SELECT * FROM zipfile( readfile('test.zip') );

表值函数为 zip 存档中的每条记录(文件、目录或符号链接)返回一行。每行都有以下列:

Column NameContents
name File name/path for the zip file record.
mode UNIX mode, as returned by stat(2) for the zip file record (an integer). This identifies the type of record (file, directory or symbolic link), and the associated user/group/all permissions.
mtime UTC timestamp, in seconds since the UNIX epoch (an integer).
sz Size of associated data in bytes after it has been uncompressed (an integer).
rawdata Raw (possibly compressed) data associated with zip file entry (a blob).
data If the compression method for the record is either 0 or 8 (see below), then the uncompressed data associated with the zip file entry. Or, if the compression method is not 0 or 8, this column contains a NULL value.
method The compression method used to compress the data (an integer). The value 0 indicates that the data is stored in the zip archive without compression. 8 means the raw deflate algorithm.

3.2. 虚拟表接口(读/写访问)

为了创建或修改现有的 zip 文件,必须在数据库模式中创建一个“zipfile”虚拟表。CREATE VIRTUAL TABLE 语句需要 zip 文件的路径作为其唯一参数。例如,要写入当前目录中的 zip 文件“test.zip”,可以使用以下命令创建一个 zipfile 表:

CREATE VIRTUAL TABLE temp.zip USING zipfile('test.zip');

这样的虚拟表具有与上一节中描述的表值函数相同的列。可以使用 SELECT 语句以与表值函数相同的方式读取它。

使用虚拟表接口,可以通过将新行插入虚拟表来将新条目添加到 zip 存档中。可以通过删除行来删除条目,也可以通过更新行来修改条目。

3.2.1. 将条目添加到 Zip 存档

可以通过插入新行将条目添加到 zip 存档中。最简单的方法是仅指定“名称”和“数据”列的值,并让 zipfile 为其他字段填充合理的默认值。要将目录插入存档,请将“数据”列设置为 NULL。例如,要将目录“dir1”和包含文本“abcdefghi”的文件“m.txt”添加到 zip 存档“test.zip”:

INSERT INTO temp.zip(name, data) VALUES('dir1', NULL);           -- Add directory 
INSERT INTO temp.zip(name, data) VALUES('m.txt', 'abcdefghi');   -- Add regular file 

插入目录时,如果“name”值不以“/”字符结尾,则 zipfile 模块会附加一个。这对于与处理 zip 存档的其他程序(最值得注意的是“info-zip”)的兼容性是必要的。

要插入符号链接,用户还必须提供“模式”值。例如,要添加从“link.txt”到“m.txt”的符号链接:

INSERT INTO temp.zip(name, mode, data) VALUES('link.txt', 'lrwxrw-rw-', 'abcdefghi');

以下规则和注意事项适用于作为每个 INSERT 语句的一部分指定的值:

Columns Notes
name A non-NULL text value must be specified for the name column. It is an error if the specified name already exists in the archive.
mode If NULL is inserted into the mode column, then the mode of the new archive entry is automatically set to either 33188 (-rw-r--r--) or 16877 (drwxr-xr-x), depending on whether or not the values specified for columns "sz", "data" and "rawdata" indicate that the new entry is a directory.

If the specified value is an integer (or text that looks like an integer), it is inserted verbatim. If the value is not a valid UNIX mode, some programs may behave unexpectedly when extracting files from the archive.

Finally, if the value specified for this column is not an integer or a NULL, then it is assumed to be a UNIX permissions string similar to those output by the "ls -l" command (e.g. "-rw-r--r--", "drwxr-xr-x" etc.). In this case, if the string cannot be parsed it is an error.
mtime If NULL is inserted into the mtime column, then the timestamp of the new entry is set to the current time. Otherwise, the specified value is interpreted as an integer and used as is.
sz This column must be set to NULL. If a non-NULL value is inserted into this column, or if a new non-NULL value is provided using an UPDATE statement, it is an error.
rawdata This column must be set to NULL. If a non-NULL value is inserted into this column, or if a new non-NULL value is provided using an UPDATE statement, it is an error.
data To insert a directory into the archive, this field must be set to NULL. In this case if a value was explicitly specified for the "mode" column, then it must be consistent with a directory (i.e. it must be true that (mode & 0040000)=0040000).

Otherwise, the value inserted into this field is the file contents for a regular file, or the target of a symbolic link.
method This field must be set one of integer values 0 and 8, or else to NULL.

For a directory entry, any value inserted into this field is ignored. Otherwise, if it is set to 0, then the file data or symbolic link target is stored as is in the zip archive and the compression method set to 0. If it is set to 8, then the file data or link target is compressed using deflate compression before it is stored and the compression method set to 8. Finally, if a NULL value is written to this field, the zipfile module automatically decides whether or not to compress the data before storing it.

不支持将 rowid 字段的显式值指定为 INSERT 语句的一部分。提供的任何值都将被忽略。

3.2.2. 删除 Zip 存档条目

通过删除相应的行,可以从现有的 zip 存档中删除记录。例如,要使用上面创建的虚拟表从 zip 存档“test.zip”中删除文件“m.txt”:

DELETE FROM temp.zip WHERE name = 'm.txt';

请注意,从 zip 存档中删除记录不会回收存档中使用的空间 - 它只会从存档“中央目录结构”中删除一个条目,使该条目无法访问。解决这种低效率问题的一种方法是根据已编辑存档的内容创建一个新的 zip 存档。例如,编辑通过虚拟表 temp.zzz 访问的存档后:

-- Create a new, empty, archive: 
CREATE VIRTUAL TABLE temp.newzip USING zipfile('new.zip');

-- Copy the contents of the existing archive into the new archive
INSERT INTO temp.newzip(name, mode, mtime, data, method)
    SELECT name, mode, mtime, data, method FROM temp.zzz;

3.2.3. 更新现有的 Zip 存档条目

可以使用 UPDATE 语句修改现有的 zip 存档条目。

zipfile 虚拟表最左边的三列,“name”,“mode”和“mtime”,可以分别设置为可以插入同一列的任何值(见上文)。如果“mode”或“mtime”设置为 NULL,则最终值将按照对 NULL 值的 INSERT 的描述来确定 - “mtime”的当前时间和“mode”的 33188 或 16877,具体取决于是否不是为 zipfile 表的下四列指定的值表示该条目是目录或文件。

尝试将 sz 或 rawdata 字段设置为 NULL 以外的任何值都是错误的。

数据和方法列也可以按照上面对 INSERT 的描述进行设置。

3.3. zipfile() 聚合函数

新的 zip 存档可以使用 zipfile() 聚合函数完全在内存中构建。聚合函数访问的每一行都会向 zip 存档添加一个条目。返回的值是一个包含整个存档图像的 blob。

可以使用 2、4 或 5 个参数调用 zipfile() 聚合函数。如果使用 5 个参数调用它,那么添加到存档的条目相当于将相同的值插入到 zipfile 虚拟表的“name”、“mode”、“mtime”、“data”和“method”列中。

如果使用 2 个参数调用 zipfile(),则添加到存档的条目等效于通过将相同的两个值插入 zipfile 虚拟表的“名称”和“数据”列而添加的条目,所有其他值设置为无效的。如果使用 4 个参数调用,则相当于将 4 个值插入到“name”、“mode”、“mtime”和“data”列中。换句话说,以下查询对是等价的:

SELECT zipfile(name, data) ...
SELECT zipfile(name, NULL, NULL, data, NULL) ...

SELECT zipfile(name, mode, mtime, data) ...
SELECT zipfile(name, mode, mtime, data, NULL) ...

例如,要创建一个包含两个文本文件“a.txt”和“b.txt”的存档,其中分别包含文本“abc”和“123”:

WITH contents(name, data) AS (
  VALUES('a.txt', 'abc') UNION ALL
  VALUES('b.txt', '123')
)
SELECT zipfile(name, data) FROM contents;