为以下项目提供图像 <img>
#
- 作者:
在本操作指南中,您将学习如何创建用于将图像提供给HTML的终结点 <img>
没有客户端Java脚本的标签。事实上,所提出的技术不仅适用于提供图像,而且适用于提供任意文件。
我们将从一个突出一般概念的最小示例开始。之后,我们将提供一个更详细的解决方案,以修复第一种方法的一些缺点。
警告
在数据库中保存二进制文件时要小心,在大多数情况下,最好为这些文件提供单独的存储服务。看见 Storing Binary files in the Database 。
最小示例#
首先,我们需要一个公用表来存储文件。
create table files(
id int primary key
, blob bytea
);
让我们假设这张表包含两只ID为42的可爱小猫的图像。我们可以使用以下命令从PostgREST API中检索二进制格式的图像 媒体类型处理程序 :
create domain "application/octet-stream" as bytea;
create or replace function file(id int) returns "application/octet-stream" as $$
select blob from files where id = file.id;
$$ language sql;
现在我们可以请求RPC端点 /rpc/file?id=42
与 Accept: application/octet-stream
标题。
curl "localhost:3000/rpc/file?id=42" -H "Accept: application/octet-stream"
不幸的是,将URL放入 src
属于一个 <img>
标记将不起作用。这是因为浏览器不会发送所需的 Accept: application/octet-stream
标题。相反, Accept: image/webp
默认情况下,许多Web浏览器都会发送Header。
幸运的是,我们可以更改函数中接受的媒体类型,如下所示:
create domain "image/webp" as bytea;
create or replace function file(id int) returns "image/webp" as $$
select blob from files where id = file.id;
$$ language sql;
现在,该图像将显示在HTML页面中:
<img src="http://localhost:3000/file?id=42" alt="Cute Kittens"/>
改进版#
基本解决方案有一些缺点:
他们的回应
Content-Type
标头设置为image/webp
。如果要为文件指定不同的格式,这可能是一个问题。下载请求(例如,右击->图像另存为)到
/files?select=blob&id=eq.42
将会提议files
作为文件名。这可能会让用户感到困惑。不缓存对二进制终结点的请求。这将对数据库造成不必要的负载。
以下改进版本解决了这些问题。首先,除了最小的示例之外,我们还需要在数据库中存储文件的媒体类型和名称。
alter table files
add column type text generated always as (byteamagic_mime(substr(blob, 0, 4100))) stored,
add column name text;
这使用了 byteamagic_mime()
函数从 pg_byteamagic extension 若要在 files
桌子。要猜测文件的类型,通常只需查看文件的开头就足够了,这样效率更高。
接下来,我们设置Modify函数来设置内容类型和文件名。我们利用这个机会配置一些基本的客户端缓存。对于生产环境,您可能需要配置其他缓存,例如在 reverse proxy 。
create domain "*/*" as bytea;
create function file(id int) returns "*/*" as
$$
declare headers text;
declare blob bytea;
begin
select format(
'[{"Content-Type": "%s"},'
'{"Content-Disposition": "inline; filename=\"%s\""},'
'{"Cache-Control": "max-age=259200"}]'
, files.type, files.name)
from files where files.id = file.id into headers;
perform set_config('response.headers', headers, true);
select files.blob from files where files.id = file.id into blob;
if FOUND -- special var, see https://www.postgresql.org/docs/current/plpgsql-statements.html#PLPGSQL-STATEMENTS-DIAGNOSTICS
then return(blob);
else raise sqlstate 'PT404' using
message = 'NOT FOUND',
detail = 'File not found',
hint = format('%s seems to be an invalid file id', file.id);
end if;
end
$$ language plpgsql;
这样,我们就可以从 /rpc/file?id=42
。因此,生成的HTML将是:
<img src="http://localhost:3000/rpc/file?id=42" alt="Cute Kittens"/>