1
votes

As the title implies, this is an interesting use case. I have an HTML5 Canvas that I am able to convert to a image. I am using AngularJS as the front end, with Node/Express on the backend for my site. Converting the HTML5 Canvas to an image works as intended and I am able to upload this to a separate Web Service using jQuery AJAX (not using Node/Express) as follows:

 $scope.webService = function () {
        var send = function (blob) {
            var filename = 'Test.pdf';
            var formdata = new FormData();
            formdata.append('File1', blob, filename);

            $.ajax({
                url: 'http://awebservice.net/endpoint',
                type: "POST",
                data: formdata,
                mimeType: "multipart/form-data",
                processData: false,
                contentType: false,
                crossDomain: true,
                success: function (result) {
                    console.log("Upload complete!");
                },
                error: function (error) {
                    console.log("Something went wrong!");
                }
            })
        }

        var canvasImage = document.getElementById("c");
        if (!canvasImage.toBlob) {
            var dataURL = canvasImage.toDataURL();
            var bytes = atob(dataURL.split(',')[1])
            var arr = new Uint8Array(bytes.length);
            for (var i = 0; i < bytes.length; i++) {
                arr[i] = bytes.charCodeAt(i);
            }
            send(new Blob([arr], { type: 'image/png' }));
        }
        else
            canvasImage.toBlob(send);
        }

I am using Node/Express in another scenario where I can upload an image to Azure via Node if I use a standard input form with a submit button. Here is the Node/Express code that uploads to Azure Blob Storage for an image manually uploaded by a user through an file input and submit button, where /upload is called on the form action of the HTML5 form:

app.post('/upload4', function (req, res) {
var form = new formidable.IncomingForm();
form.parse(req, function (err, fields, files) {
    var options = {
        contentType: files.myfile.type,
        metadata: { fileName: files.myfile.name }
    };
    blobSvc.createBlockBlobFromLocalFile('blob5', files.myfile.name, files.myfile.path, options, function (error, result, response) {
        if (!error) {
            // file uploaded
            res.send('file uploaded!');
        }
    });
});
console.log(req);
});

However, I need to pass my converted HTML5 Canvas "behind the scenes" where users don't have to download and re-upload the HTML5 Canvas Image via a Web form, but rather on click, the Canvas is converted and the image is uploaded to Azure (see first example above for a working example using a different Web Service that doesn't use Node/Express that uploads an HTML5 Canvas converted to an image via jQuery AJAX). Essentially, since I am using Node/Express to manage Azure Blob Storage, I want the upload functionality that works in my jQuery AJAX example.

I was envisioning that I could pass the image data/variables back to Node/Express as follows:

AngularJS Front End Controller Code (tied to a ng-click button action named "upload()"):

$scope.upload = function () {
var canvasImage = document.getElementById("c");
var img = canvasImage.toDataURL("image/png");
var filename = 'Texture_0.png';

$http.post('/upload', { filename: filename, file: img }).success(function (data) {
console.log(data);
});
}

Node/Express Backend Code:

app.post('/upload', function (req, res) {
var filename = req.filename;
var file = req.file;
blobSvc.createBlockBlobFromLocalFile('mycontainer', filename, file, function (error, result, response) {
    if (!error) {
        console.log("Uploaded" + result);
// file uploaded
    }
    else {
        console.log(error);
    }
});
});

When I click the "magic button" to convert the Canvas to an image (image conversion works) and upload (upload doesn't work), I get a Node error as follows:

Error: Required argument blob for function createBlockBlobFromFile is not defined

I also logged "filename" and "file" to the Node console and got "undefined".

Client side I get a 500 Internal Server Error in the console of my browser. However, using the Network tab I dug deeper and saw that the "Request Payload" had the "file" base64 encoded and the "filename" as correct.

I largely suspect that I am not passing in the variables to Node/Express properly, and that is what is tripping up the Azure call. I am also wondering if since uploading an image via an input form works with Azure/Node, if I should somehow pass the converted Canvas as formdata but unsure how I would achieve this/should attempt it.

My question is, how do I pass the right variables/data so my canvas can be uploaded to Azure Blob Storage from AngularJS front end to Node/Express backend?

1

1 Answers

2
votes

I had a test project to work out your requirements. And there are several key points I think should be noticed.

Node/Express Backend part:

To get POST data in Express, we can leverage bodyParser middle-ware which is used to be internal part of Express framework.

var bodyParser = require('body-parser');
var router = express.Router();
router.use(bodyParser.json());
router.use(bodyParser.urlencoded({ extended: false }));
router.post('/postCanvas', function (req, res, next){
   var filename = req.body.filename;
    var file = req.body.file;
    var base64Data;
    fileBuffer = decodeBase64Image(file);
   blobsrv.createBlockBlobFromText('container', filename, fileBuffer.data, {'contentType': fileBuffer.type}, function (error, result, response) {
        if (!error) {
            console.log("Uploaded" + result);
       }
        else {
            console.log(error);
        }
    });
    /*
    fs.writeFileSync('upload/' + filename, fileBuffer.data);
    blobsrv.createBlockBlobFromLocalFile('container', filename, 'upload/' + filename, function (error, result, response) {
        if (!error) {
            console.log("Uploaded" + result);
        // file uploaded
        }
        else {
            console.log(error);
        }
    });*/
})
function decodeBase64Image(dataString) {
    var matches = dataString.match(/^data:([A-Za-z-+\/]+);base64,(.+)$/),
        response = {};

    if (matches.length !== 3) {
        return new Error('Invalid input string');
    }

    response.type = matches[1];
    response.data = new Buffer(matches[2], 'base64');

    return response;
}

The uncomment part will directly create a blob by base64 string, and the comment part will save the image as a file on server first, then create a blob by the file.

Maybe you will meet CROS problem if your server is hosted on Azure. So we need to allow corss-origin resource sharing in express.js framework.

Here is my code snippet:

app.use(function (req, res, next) {
    res.setHeader("Access-Control-Allow-Origin", "http://localhost:8801");
    res.setHeader("Access-Control-Allow-Headers", "Origin, X-Requested-With,   Content-Type, Accept");
    res.setHeader('Access-Control-Allow-Credentials', true);
    res.setHeader('Access-Control-Allow-Methods', 'POST, GET, PUT, DELETE, OPTIONS');
    next();
});

The angular part is the same with you:

$scope.upload = function () {
        var canvasImage = document.getElementById("myCanvas");
        var img = canvasImage.toDataURL("image/jpeg");
        var filename = 'Texture_0.jpg';
        console.log(img);
        console.log(filename);
        $http.post('http://localhost:1337/postCanvas', { filename: filename, file: img }).success(function (data) {
            console.log(data);
        },function(err){
            console.log(err);
        });
    }