Thanks to some fabulous work by @akash-akya in the new Vix version 0.11.0, Image version 0.4.0 is now released with a focus on image streaming.
Changelog
-
Adds support for opening streaming images. This allows images to be streamed from File.stream!/3
or from any t:Enumerable.t/0
including those created from ExAws.S3 by ExAws.stream!/2.
-
Adds support writing streaming images. This allows images to be streamed as an enumerable which can then be consumed by Plug.Conn.chunk/2
, by ExAws.S3.upload/3
, File.stream/3
or any other function that processes stream resources. See the test/stream_image_test.exs
file for examples.
-
Adds a :memory
option to Image.write/3
. Instead of a path name or stream, use :memory
if youād like to return a binary
form of an image in its formatted type. Note that this will run the image transformation pipeline resulting in the entire final image being loaded into memory. Therefore this option should be used sparingly since most use cases do not require this option. It is primarily added to facilitate passing images to other libraries in the Elixir ecosystem.
Use case
One of the benefits of libvips, and therefore Vix
, is that transformations are built in pipelines. Its actually quite a lot like Elixir pipelines in nature (albeit the implementation is wildly different).
This means we can support streaming an image into Image.open/2
, apply a pipeline of transformations and stream the image out with Image.write/3
or Image.stream/2
. These operations all happen concurrently: streaming the image into the pipeline, the pipeline itself, and the streaming out again. Very memory efficient, very Elixir friendly, very easy to apply in an application.
Example
Imagine an example where we want to stream an image from S3, apply some transformations, and stream the image to an HTTP client. Hereās the code:
ExAws.S3.download_file("images", "Hong-Kong-2015-07-1998.jpg", :memory)
|> ExAws.stream!()
|> Image.open!()
|> Image.resize!(200)
|> Image.write(conn, suffix: ".jpg")
Here conn
is the conn
of a Plug/Phoenix request. All the machinery of streaming is abstracted away. Some additional headers would need to be set for the MIME type and filename but apart from that there is nothing to do.
Another approach makes the stream more explicit but ultimately is the same example as that above:
ExAws.S3.download_file("images", "Hong-Kong-2015-07-1998.jpg", :memory)
|> ExAws.stream!()
|> Image.open!()
|> Image.resize!(200)
|> Image.stream!(suffix: ".jpg")
|> Enum.reduce_while(conn, fn (chunk, conn) ->
case Plug.Conn.chunk(conn, chunk) do
{:ok, conn} ->
{:cont, conn}
{:error, :closed} ->
{:halt, conn}
end
end)
Next steps
The next release will focus on interoperability with eVision and hopefully also Nx. This will enable interaction with the very cool axon and axon_onnx.
Feedback and suggestions
After the next release, primary development will be user-driven. Open an issue, start a discussion or comment here with what youād like to see.