【问题标题】:Download file via Webservice and Push it to Azure Blob Storage via Node/Express通过 Web 服务下载文件并通过 Node/Express 将其推送到 Azure Blob 存储
【发布时间】:2015-09-18 12:32:42
【问题描述】:

我需要从供应商的 Web 服务中检索文件并将这些文件推送到唯一的 blob 容器中,以便用户拥有唯一的“工作区”。本质上,我会从供应商那里获取文件,用户可以通过他们自己的 blob 容器中的文件来编辑这些文件,这样他们就不会相互交叉工作文件。我有独特的 blob 容器工作,但需要从我的供应商 API “下载”/获取文件并将它们推送到 blob 容器中。我能够成功检索文件,这将是获取 PDF、文本文件和图像的单独调用......但如果我尝试将它们上传到 Azure Blob 存储,我在 Node.js 中收到以下错误:

TypeError: 无法读取 null 的属性“长度”

我认为我需要在客户端将文件编码为 base64 以正确获取长度,并且已经看到了一些将 Canvas 与 toDataURL 一起使用的示例,但我不确定这是否是本质上下载和直接推送到 Azure Blob 存储,特别是因为我有 PDF 等文件(不确定 PDF 是否可以进行 base64 编码)。

这是调用服务的 AngularJS 控制器(请注意,实际端点可能会根据它们调用的文件而改变,因此我使用客户端 GET 文件来控制用户可能在表单中输入的变量):

$scope.getFiles = function () {

$.ajax({
url: 'http://vendorwebservice.net/ws/file1',
type: "GET",
success: function (result) {
console.log(result);
var filename = 'Texture_0.png';

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

alert("Files Retrieved!");
},
error: function (error) {
console.log("Failed to download image!");
}
})
}

这是我的后端/节点/Express 代码:

app.post('/postFile', function (req, res, next) {
    var filename = req.body.filename;
    var file = req.body.file;
    var base64Data;
    fileBuffer = decodeBase64Image(file);
    blobSvc.createBlockBlobFromText('blob5', filename, fileBuffer.data, { 'contentType': fileBuffer.type }, function (error, result, response) {
        if (!error) {
            console.log("Uploaded" + result);
        }
        else {
            console.log(error);
        }
    });
})

// Decode file for upload
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;
}

更新 1: 根据 Gary 的建议,我尝试了以下方法,但代码有点混乱,因为我的供应商 API 没有文件 URI,而是在 GET 上生成文件的端点(又名,我不知道如何将端点传递给 Gary 的有意义的例子)。例如,我的供应商端点“http://vendorapi.net/ws/texture_0”返回一个名为“Texture_0.png”的文件。

前端 Angular 代码:

 $scope.getFromVendor = function () {
            var filename = 'Texture_0.png';//jpg,txt...
            $http.post('/uploadapifiles', { filename: filename, url: 'http://vendorapi.net/ws/texture_0' }).success(function (data) {
                console.log(data);
            }, function (err) {
                console.log(err);
            });
        }

服务器端下载处理(我相信这是最混乱的一个:

app.get(http://vendorapi.net/ws/texture_0', function (req, res, next) {
    res.download('http://vendorapi.net/ws/texture_0' + req.params.filename);
})

服务器端上传处理:

app.post('/uploadapifiles', function (req, res, next) {

    var filename = req.body.filename;
    var r = request(req.body.url).pipe(fs.createWriteStream('http://vendorapi.net/ws/texture_0' + filename))
    r.on('close', function () {
        blobsrv.createBlockBlobFromLocalFile('blob5', filename, 'http://vendorapi.net/ws/texture_0' + filename, function (error, result, response) {
            if (!error) {
                console.log("Uploaded" + result);
            }
            else {
                console.log(error);
            }
        });
    })
});

【问题讨论】:

  • 实际上,我们使用toDataURL从图像中得到的数据是base64编码类型,应该以data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIA...开头。并且测试和PDF文件不同,所以直接使用decodeBase64Image可能会出错。所以我们应该根据不同的文件类型分别处理。您是否可以提供获取文件的 URL,因为我无法确定从 URL 中会得到什么。
  • 我无法发布 API,但如果有帮助,我可以将其注销。
  • 用图像尝试我的代码会产生同样的错误
  • 但是在这个场景中,你上传图片数据的方式和你昨天发布的场景不同。那么有 2 个单独的场景吗?
  • 是的。昨天是从 Canvas 上传的,而这个需求是上传我从供应商 API 中“获取”的文件

标签: angularjs node.js azure express


【解决方案1】:

在您最初的想法中,首先您在客户端获取文件内容数据,然后将数据发布到 Express Web 服务器。

如果你得到的文件很大,它会降低你的网站速度,因为文件数据会通过 HTTP 传输两次,并且可能会出现其他问题。

另外,在我的测试项目中,很难直接处理文件内容数据。

所以我尝试了另一个想法作为解决方法。

我只是将获取特定文件的API发布到服务器,将文件保存为服务器目录上的文件并将文件上传到服务器端的存储。 这是我的代码 sn-p:

角度前端:

$scope.upload =function(){
    var filename = (new Date()).getTime()+'.pdf';//jpg,txt...
    $http.post('http://localhost:1337/uploadfile', { filename: filename, url: 'http://localhost:1337/output/123.pdf'}).success(function (data) {
        console.log(data);
    },function(err){
        console.log(err);
    });
  }

后端:

我怀疑你获取文件格式的 API 会像后面一样。

router.get('/output/:filename', function (req, res, next) {
    res.download('upload/'+req.params.filename);
})

post 请求处理程序利用包request,无需确定文件类型或编码类型,createBlockBlobFromLocalFile 会将文件上传到您提供的 blob 存储位置,API reference

router.post('/uploadfile', function (req, res, next) {
    var request = require('request');
    var filename = req.body.filename;
    var tmpFolder = 'upload/', //this folder is to save files download from vendor URL, and should be created in the root directory previously. 
        tmpFileSavedLocation = tmpFolder + filename; //this is the location of download files, e.g. 'upload/Texture_0.png'
    var r = request(req.body.url).pipe(fs.createWriteStream(tmpFileSavedLocation))//this syntax will download file from the URL and save in the location asyns
   r.on('close', function (){
        blobsrv.createBlockBlobFromLocalFile('vsdeploy', filename, tmpFileSavedLocation, function (error, result, response) {
            if (!error) {
                console.log("Uploaded" + result);
           }
            else {
                console.log(error);
            }
        });
    })

})

【讨论】:

  • 这种方法很有意义。我面临的一个挑战是我的供应商提供的 Web 服务 API 没有提供 URI,而是提供了一个端点,然后下载文件(例如,调用“vendorwebservice.net/ws/file1”下载 myfile.pdf)。
  • 后端代码的第一部分sn -p也提供了一个API来下载文件。例如,在我的项目的浏览器中浏览http://localhost:1337/output/123.pdf,它将下载文件但在浏览器上显示文件的内容。所以大家可以先试一试,有什么不明白的,我们以后再讨论。
  • 谢谢 Gary,我之前已经试过了。我更新的代码在我的原始帖子中。这会产生 throw "er; // Unhandled stream error in pipe." 的错误。因为我的端点不是 URI,而是最后没有真实文件的 URL,我相信我把代码搞砸了/有点迷路了。
  • 另外,upload/应该调用另一个Express函数吗?
  • 您对output/filename 感到困惑。 createWriteStream() 中的 output/filename 是保存从供应商 url 下载的文件的文件路径。见API reference。我已经编辑了我的答案,我修改了代码 sn-p 并添加了一些 cmets。伊戈尔router.get('/output/:filename', function (req, res, next) { res.download('upload/'+req.params.filename); })
猜你喜欢
  • 2019-11-05
  • 1970-01-01
  • 2020-03-02
  • 1970-01-01
  • 2022-07-07
  • 2022-01-12
  • 2019-05-29
  • 1970-01-01
  • 2017-01-02
相关资源
最近更新 更多