Node.js 使用Node/Express将转换为图像的HTML5画布上载到Azure

Node.js 使用Node/Express将转换为图像的HTML5画布上载到Azure,node.js,azure,express,Node.js,Azure,Express,正如标题所暗示的,这是一个有趣的用例。我有一个HTML5画布,我可以将其转换为图像。我使用AngularJS作为前端,我的站点的后端是Node/Express。将HTML5画布转换为图像可以按预期工作,我可以使用jQuery AJAX(不使用Node/Express)将其上传到单独的Web服务,如下所示: $scope.webService = function () { var send = function (blob) { var filename

正如标题所暗示的,这是一个有趣的用例。我有一个HTML5画布,我可以将其转换为图像。我使用AngularJS作为前端,我的站点的后端是Node/Express。将HTML5画布转换为图像可以按预期工作,我可以使用jQuery AJAX(不使用Node/Express)将其上传到单独的Web服务,如下所示:

 $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);
        }
但是,我需要将转换后的HTML5画布传递到“幕后”,用户不必通过Web表单下载和重新上传HTML5画布图像,而是单击,画布被转换,图像被上传到Azure(请参阅上面的第一个示例,了解使用不同Web服务的工作示例,该Web服务不使用Node/Express,该Web服务通过jQuery AJAX上传转换为图像的HTML5画布)。本质上,由于我使用Node/Express来管理Azure Blob存储,因此我希望在jQuery AJAX示例中使用上传功能

我设想可以将图像数据/变量传回Node/Express,如下所示:

 $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);
        }
AngularJS前端控制器代码(绑定到名为“upload()”的ng点击按钮操作):

节点/快速后端代码:

当我单击“魔术按钮”将画布转换为图像(图像转换工作)并上载(上载不工作)时,我得到一个节点错误,如下所示:

 $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);
        }
错误:函数createBlockBlobFromFile的必需参数blob为 未定义

我还将“filename”和“file”记录到节点控制台,得到了“undefined”

客户端我在浏览器的控制台中收到一个500内部服务器错误。然而,使用网络选项卡,我深入挖掘,发现“请求负载”的“文件”base64编码为“文件名”,并且“文件名”正确无误

我很大程度上怀疑我没有正确地将变量传递给Node/Express,这就是Azure调用的绊脚石。我还想知道,既然通过输入表单上传图像可以与Azure/Node一起使用,我是否应该以某种方式将转换后的画布作为formdata传递,但不确定我将如何实现这一点/应该尝试这样做


我的问题是,如何传递正确的变量/数据,以便将画布从AngularJS前端上传到Node/Express后端的Azure Blob存储?

我有一个测试项目来解决您的需求。我认为有几个关键点应该注意

节点/Express后端部分:

为了在Express中获取POST数据,我们可以利用
bodyParser
中间件,它是Express框架的内部部分

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;
}
取消注释部分将直接通过base64字符串创建blob,注释部分将首先在服务器上将图像保存为文件,然后通过该文件创建blob

如果您的服务器托管在Azure上,您可能会遇到CRO问题。因此,我们需要在
express.js
框架中允许corss源资源共享

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;
}
以下是我的代码片段:

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();
});
角度部分与您相同:

$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);
        });
    }

非常好的解释,Gary,非常感谢代码示例。我将尽快对其进行测试。两个快速问题。为什么使用router.post和app.post?使用FromLocalFile和FromText之间有什么区别吗?我相信,因为我使用的是Express 4.0,所以我不需要使用路由器:解决它。切换到以下内容(不使用路由器)非常感谢@Gary Liu.app.use(bodyParser.json());app.use(bodyParser.urlencoded({extended:false}));app.post('/postanvas',函数(req,res,next){事实上,我提供了一个属于路由器部分的代码片段,不能通过单个文件运行。中显示了该体系结构。很抱歉,我没有解释清楚。无论你怎么想,恭喜。太棒了。这非常有意义。感谢后续和参考体系结构