Week 5: Serving Over HTTPS

This week we learned how to encrypt traffic between clients and our servers by implementing HTTPS with a certificate (from a certificate authority that has verified our DNS identity) and a private key. It was perfect timing for my photo booth app: I need HTTPS enabled in order for folks to access their webcams. As a quick refresher, my app snaps a photo of an individual as they view another picture. Afterwards a diptych of both images renders on the screen. After testing HTTPS locally and also on my Digital Ocean Droplet (my server), I set to work on the next phase of my project’s development. 

Though I completed most of my photo booth's general workflow in P5 last week, I needed to figure out how to save the resulting diptych. Before setting out to save it to my server, I focused on saving it to disk. For this I learned that I could store my canvas in a variable and from that copy the specific area of the diptych to then automatically download to my computer with P5's save(). Here's the relevant snippet (the full code is available on GitHub):

function setup() {
  canvas = createCanvas(windowWidth, windowHeight);
}

function displayDiptych() {
    let doubleFrameX = windowWidth / 6;
    let doubleFrameY = (windowHeight - 4 * windowWidth / 9) / 2; 
    let doubleWidth = windowWidth * 2 / 3;
    let doubleHeight = 4 * windowWidth / 9;
  
    diptych = createGraphics(doubleWidth, doubleHeight);
    diptych.copy(canvas, doubleFrameX, doubleFrameY, doubleWidth, doubleHeight, 0, 0, doubleWidth, doubleHeight);
    save(diptych, "diptych_" + time + ".jpg");
}

But for this project to live on my server, I needed a way send the image data there and direct it to the proper folder. This was not a simple task for me to figure out. Fortunately in office hours, Shawn suggested appending elt to my diptych Graphics object in order to use the toDataURL() method.

diptych = createGraphics(doubleWidth, doubleHeight);
diptych.copy(canvas, doubleFrameX, doubleFrameY, doubleWidth, doubleHeight, 0, 0, doubleWidth, doubleHeight);

let imageString = diptych.elt.toDataURL();
console.log(imageString);

This encodes the image into machine readable format known as Base64 produced perhaps the longest string of characters I have ever seen in my browser's JavaScript console—around 600 pages if you were to print it out. To ensure that this was indeed image data, we pasted the string into this online decoder where it returned my original diptych in picture form. Magic! It was truly like being the analog darkroom for the first time to see that incredibly long string turn into a photograph before my eyes and easily the highlight of my week. 

Buy how to send this data to my server? I could not get the P5 httpPost method to work, but fortunately, I found an AJAX/JQuery example on Stack Overflow that did:

let imageString = diptych.elt.toDataURL();
saveDiptych(imageString);

function saveDiptych(img){
  let url = "https://emn200.itp.io:443/save";

  $.ajax(
    {
        url: url,
        type: "POST",
        crossDomain: true,
        dataType: "json",
        data:
        {
            image: img
        },
        success: function(response)
        {
            console.log(response); 
        },
        error: function(response)
        {
            console.log(response);
        }
    });
}

But not immediately. Initially, I received the error message in my server's console, “request entity too large.” However, the fact that my server was still seeing the request gave me hope. A search lead me to this suggestion to increase the default limit of the body parser (a module I installed to parse incoming data server-side), and this did the trick.

var bodyParser = require('body-parser');
var urlencodedParser = bodyParser.urlencoded({ extended: true }); 

var express = require('express')
var app = express()

app.use(express.static('public'));

app.use(bodyParser.json({limit: '50mb'}));
app.use(bodyParser.urlencoded({limit: '50mb', extended: true}));

When I console-logged the data of the request on the server side and saw that long string of characters appear (aka my image data) it was easily the second highlight of my week. 

Screen Shot 2018-03-01 at 9.46.56 AM.png

Now that I could send the image string to my server, I needed to decode it back to binary format (jpeg or png and human-readable with the right viewer) and save the file in a directory. This was accomplished by these lines of code, again thanks to Shawn:

app.post('/save', function (req, res) {
  var data = req.body.image;

  var searchFor = 'data:image/jpeg;base64,';
  var strippedImage = data.slice(data.indexOf(searchFor) + searchFor.length);
  var binaryImage = new Buffer(strippedImage, 'base64');

  var timeStamp = Math.floor(Date.now() / 1000);

  fs.writeFileSync(__dirname + '/public/diptychs/diptych_' + timeStamp + '.jpg', binaryImage);
})

Client and server code on GitHub

I still have quite a bit to do for my app. How will I display all the diptychs? All at once in a gallery or individually by session? And the styling?! Though it's a minimal design, I still need to make decisions about how I want the final piece to look. And of course there remains the question of which batch of images to curate for the booth. Time to get started...