Use a processed image, placeholder, or image information inside your application by requesting its signed URL with an HTTP client. This guide assumes your application builds URLs for an ImagePipe server running elsewhere (see Building URLs for the server).
image_pipe_url has no fetch function. The signed URL is the interface, so
any HTTP client works. The examples use Req 0.8,
currently a release candidate. Req.stream/4 needs it.
The server can only process images it reads from its own sources. An image that exists only in your application, such as an upload, has to be stored somewhere the server reads first, because the server has no upload endpoint.
Build URLs for the internal address
The signature covers only the part of the URL after the base URL, so a URL
signed for your CDN is also valid at the server's internal address. To skip
the CDN for server-side requests, build a second URL configuration with the
same keys and the server's internal address as base_url:
internal = ImagePipe.URL.config(
base_url: "http://image-service:4000/images",
keys: signing_keys
)
url =
ImagePipe.URL.new(internal)
|> ImagePipe.URL.output(terminal: :lqip_css)
|> ImagePipe.URL.url!(source)Use the same settings as your public configuration otherwise, such as
source_encryption_keys and encrypt_source.
Choose the format
Without a format in the plan, the server returns AVIF or WebP when the
request's Accept header lists it, and otherwise the original's format where
possible. Set format: with ImagePipe.URL.output/2 to get one fixed
format.
Buffer small results
Read placeholders, image information, and thumbnails into memory:
case Req.get(url,
decode_body: false,
receive_timeout: 30_000,
headers: [accept: "image/avif,image/webp"]
) do
{:ok, %Req.Response{status: 200, body: body} = response} ->
[content_type] = Req.Response.get_header(response, "content-type")
{:ok, body, content_type}
{:ok, %Req.Response{status: status}} ->
{:error, {:http_status, status}}
{:error, exception} ->
{:error, exception}
endFor the lqip_css URL above, this returns
{:ok, "#ddd97765", "text/plain; charset=utf-8"}.
decode_body: falsekeeps every body as raw bytes, including the JSON of image information.- Req returns error responses as
{:ok, response}, so check the status. A4xxmeans the server rejected the request: an invalid option (400), a bad signature (403), a missing source (404), or an expired URL (410). Retrying won't help. Req retries transient failures, such as a503, by default. - Set
receive_timeoutabove the time your slowest image takes to process the first time. Cached results return quickly.
Stream large results to a file
Write large images to a file instead of holding them in memory:
case Req.get(url,
decode_body: false,
receive_timeout: 60_000,
into: File.stream!(path)
) do
{:ok, %Req.Response{status: 200}} ->
:ok
{:ok, %Req.Response{status: status}} ->
{:error, {:http_status, status}}
{:error, exception} ->
File.rm(path)
{:error, exception}
endReq writes to the file only for a 200 response. A connection that fails
partway leaves part of the image in the file, so remove it on an error.
Pass chunks onward
To forward chunks as they arrive, for example to a Plug.Conn already
switched to a chunked response, use Req.stream/4. Its function gets the
response status with each chunk, so it can stop before forwarding an error
body:
result =
Req.stream(url, conn, fn
chunk, %Req.Response{status: 200}, conn ->
{:ok, conn} = Plug.Conn.chunk(conn, chunk)
{:cont, conn}
_chunk, _response, conn ->
{:halt, conn}
end, decode_body: false)
case result do
{:ok, %Req.Response{status: 200}, conn} -> {:ok, conn}
{:ok, %Req.Response{status: status}, conn} -> {:error, {:http_status, status}, conn}
{:error, exception, _response, conn} -> {:error, exception, conn}
endNext steps
- Shared URL settings: the settings your application and the server must match.
- Error responses: every error status and what causes it.
- Output and encoding: placeholders, image information, and output formats.