Creating and streaming images with Node
Having gone over the main strategies for initiating and perting streams of data, let's practice the theory by creating a service to stream (aptly named) PNG (Portable Network Graphics) images to a client. This will not be a simple file server, however. The goal is to create PNG data streams by piping the output stream of an ImageMagick convert operation executing in a separate process into the response stream of an HTTP connection, where the converter is translating another stream of SVG (Scalable Vector Graphics) data generated within a virtualized DOM (Document Object Model), existing in the Node runtime. Let's get started.
The full code for this example can be found in your code bundle.
Our goal is to use Node to generate pie charts dynamically on a server based on client requests. A client will specify some data values, and a PNG representing that data in a pie will be generated. We are going to use the D3.js library, which provides a Javascript API for creating data visualizations, and the jsdom NPM package, which allows us to create a virtual DOM within a Node process. Additionally we'll use ImageMagick to transform a SVG (Scalable Vector Graphics) representation into a PNG (Portable Network Graphics) representation.
Visit https://github.com/tmpvar/jsdom to learn about how jsdom works, and https://d3js.org/ to learn about using D3 to generate SVG.
Additionally, the PNG we create will be written to a file. If future requests pass the same query arguments to our service, we will then be able to rapidly pipe the existing rendering immediately, without the overhead of regenerating it.
A pie graph represents a range of percentages whose sum fills the total area of a circle, visualized as slices. Our service will draw such a graph based on the values a client sends. In our system, the client is required to send values adding up to 1, such as .5, .3, .2. Our server, when it receives a request, will therefore need to fetch query parameters as well as create a unique key that maps to future requests with the same query parameters:
let values = url.parse(request.url, true).query['values'].split(",");
let cacheKey = values.sort().join('');
Here, we see the URL module in action, pulling out our data values. As well, we create a key on these values by first sorting the values, then joining them into a string we will use as the filename for our cached pie graph. We sort values for this reason: the same graph is achieved by sending .5 .3 .2 and .3 .5 .2. By sorting and joining, these both become the filename .2 .3 .5.
In a production application, more work would need to be done to ensure that the query is well formed, is mathematically correct, and so on. In our example, we assume proper values are being sent.