I received an email recently with a question about using R in the common gateway interface (CGI) framework to create and pass image data to the web browser. I posted to stackoverflow about this some time ago, but had forgotten the details. The trouble is that R's graphics devices only write image data to file, rather than to a buffer or R connection. Hence, when passing image data via CGI, it is necessary to write an intermediary image file, read the file, and then output the data to STDOUT. Unfortunately, R will not allow you to write binary data to STDOUT (for example, try writeBin(as.raw("0xFF"), stdout()). So, this becomes even more complicated! One solution is to open a (binary) pipe connection to the Linux cat command, which sends it's output to STDOUT, and write the image data to the pipe connection. Of course, this is a platform dependent solution. Below is a CGI script that implements this:
#!/bin/sh REXEC="/usr/local/bin/R --vanilla --slave" $REXEC <<EOF # create a random file name pngfile <- paste0(format(Sys.time(), "%Y%m%d%H%M%S"), paste(sample(letters,10), collapse=""), ".png") # create temporary graphic file png(pngfile, type = "cairo") x <- seq(-10, 10, length= 30) y <- x f <- function(x, y) { r <- sqrt(x^2+y^2); 10 * sin(r)/r } z <- outer(x, y, f) z[is.na(z)] <- 1 op <- par(bg = rgb(1,1,1,0)) persp(x, y, z, theta = 30, phi = 30, expand = 0.5, col = "lightblue") persp(x, y, z, theta = 30, phi = 30, expand = 0.5, col = "lightblue", ltheta = 120, shade = 0.75, ticktype = "detailed", xlab = "X", ylab = "Y", zlab = "Sinc( r )") invisible(dev.off()) # write headers pngsize <- file.info(pngfile)[["size"]] cat("Content-type: image/png\n") cat(paste("Content-length: ", pngsize, "\n\n", sep="")) # open pipe to stdout and pass image data con <- pipe("cat", "wb") writeBin(readBin(pngfile, 'raw', n=pngsize), con) flush(con) close(con) # remove intermediate graphic invisible(file.remove(pngfile)) EOF ###