From 334dda611c4230c3c3758c4b051b14eacf294077 Mon Sep 17 00:00:00 2001 From: Patrik Keller
Date: Tue, 17 Mar 2020 18:59:40 +0100
Subject: [PATCH] Tutorial: Providing images for (#307)
---
how-tos/providing-images-for-img.rst | 126 +++++++++++++++++++++++++++
index.rst | 1 +
2 files changed, 127 insertions(+)
create mode 100644 how-tos/providing-images-for-img.rst
diff --git a/how-tos/providing-images-for-img.rst b/how-tos/providing-images-for-img.rst
new file mode 100644
index 000000000..8bb510087
--- /dev/null
+++ b/how-tos/providing-images-for-img.rst
@@ -0,0 +1,126 @@
+Providing images for
+==========================
+
+:author: `pkel
` tags without client side javascript.
+The resulting HTML might look like this:
+
+.. code-block:: html
+
+
+
+In fact, the presented technique is suitable for providing not only images, but arbitrary files.
+
+We will start with a minimal example that highlights the general concept.
+Afterwards we present are more detailed solution that fixes a few shortcomings of the first approach.
+
+Minimal Example
+---------------
+
+PostgREST returns binary data on requests that set the :code:`Accept: application/octet-stream` header.
+The general idea is to configure the reverse proxy in front of the API to set this header for all requests to :code:`/files/`.
+We will show how to achieve this using Nginx.
+
+First, we need a public table for storing the files.
+
+.. code-block:: postgres
+
+ create table files(
+ id int primary key
+ , blob bytea
+ );
+
+Let's assume this table contains an image of two cute kittens with id 42.
+We can retrieve this image in binary format from our PostgREST API by requesting :code:`/files?select=blob&id=eq.42` with the :code:`Accept: application/octet-stream` header.
+Unfortunately, putting the URL into the :code:`src` of an :code:`` tag will not work.
+That's because browsers do not send the required header.
+
+Luckily, we can configure our `Nginx reverse proxy <../admin.html>`_ to fix this problem for us.
+We assume that PostgREST is running on port 3000.
+We provide a new location :code:`/files/` that redirects requests to our endpoint with the :code:`Accept` header set to :code:`application/octet-stream`.
+
+.. code-block:: nginx
+
+ server {
+ # rest of reverse proxy and web server configuration
+ ...
+
+ location /files/ {
+ # /files/
` tag should now work as expected.
+
+Improved Version
+----------------
+
+The basic solution has some shortcomings:
+
+1. The response :code:`Content-Type` header is set to :code:`application/octet-stream`.
+ This might confuse clients and users.
+2. Download requests (e.g. Right Click -> Save Image As) to :code:`files/42` will propose :code:`42` as filename.
+ This might confuse users.
+3. Requests to the binary endpoint are not cached.
+ This will cause unnecessary load on the database.
+
+The following improved version addresses these problems.
+First, we store the media types and names of our files in the database.
+
+.. code-block:: postgres
+
+ create table files(
+ id int primary key
+ , type text
+ , name text
+ , blob bytea
+ );
+
+Next, we set up an RPC endpoint that sets the content type and filename.
+We use this opportunity to configure some basic, client-side caching.
+For production, you probably want to configure additional caches, e.g. on the reverse proxy.
+
+.. code-block:: postgres
+
+ create function file(id int) returns bytea 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
+ 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;
+
+With this, we can obtain the cat image from :code:`/rpc/file?id=42`.
+Consequently, we have to replace our previous rewrite rule in the Nginx recipe with the following.
+
+.. code-block:: nginx
+
+ rewrite /files/([^/]+).* /rpc/file?id=$1 break;
diff --git a/index.rst b/index.rst
index 75f5c5bc1..0f7deaea9 100644
--- a/index.rst
+++ b/index.rst
@@ -126,6 +126,7 @@ These are recipes that'll help you address specific use-cases.
- :doc:`how-tos/casting-type-to-custom-json`
- :doc:`how-tos/embedding-table-from-another-schema`
+- :doc:`how-tos/providing-images-for-img`
Topic guides
------------