【问题标题】:How do I download images with Parse Cloud Code and save them in an array?如何使用 Parse Cloud Code 下载图像并将其保存在数组中?
【发布时间】:2016-05-21 02:42:35
【问题描述】:

我使用 Parse.com 作为 iOS 应用程序的后端服务,我有一列将来自 Facebook 的个人资料图片存储为数组中的 URL。

像这样:

[
    "https://z-1-scontent.xx.fbcdn.net/hp...e=5737C2D1",
    "http://files.parsetfss.com/9b0a70...40906b-newProfilePic.png",
    "http://files.parsetfss.com/9b0a70...24dd6-newProfilePic.png"
]

有时这些 URL 会过期(由于 Facebook 规则)并且不包含图像。互联网的其他方面也不建议存储这样的个人资料图片。

所以我想做的是制作一个 Parse Cloud Code Javascript,它通过从 URL 下载图像并将它们作为 Parse 文件保存在新列中的新数组中,从而将个人资料图片 URL 迁移到真实图像文件中。

这是我想做的:

  • 在 Parse 中查询所有用户
  • 获取他们的个人资料图片 URL
  • 从每个 URL 下载图片
  • 创建图像文件
  • 将图像添加到新数组中
  • 将该数组保存给用户

我希望这是有道理的。我创建了一个如下所示的 Cloud Code 函数:

Parse.Cloud.define("migrateColumn", function(request, response) {

Parse.Cloud.useMasterKey();

var usersToSave = [];
var profileImages = [];

var userQuery = new Parse.Query("User");
userQuery.find().then(function(users) {

    for (var a = 0; a < users.length; a++) {
          var user = users[a];
          var profileURLs = user.get("profilePictures");

          for (var b = 0; b < profileURLs.length; b++) {
              var url = profileURLs[b];

              Parse.Cloud.httpRequest({
                    url: url
              }).then(function(httpResponse) {
                  var imageBuffer = httpResponse.buffer;
                  var base64 = imageBuffer.toString("base64");
                  var file = new Parse.File("userProfileImage.png", { base64: base64 });

                  profileImages.push(file);
              });
          }

      user.set("profileImages", ["profileImage" + (a + 1)]);
      usersToSave.push(user);
    }
}).then(function() {
    Parse.Object.saveAll(usersToSave, {
        success: function() {
            response.success("Success : " + profileImages);

        }, error: function(error) {
            response.error("An error occurred : " + error.message);
        }
    });
});

});

我知道httpRequest 是异步完成的,我需要以某种方式实现Promise,以等待所有图像完成下载。我没有足够的 Javascript 知识来自己构建它。感谢所有帮助。

【问题讨论】:

    标签: javascript parse-platform cloud


    【解决方案1】:

    你在正确的轨道上。如果您创建执行逻辑任务并返回承诺的函数,一切都会变得更简单。例如,一个接受单个 url 并返回单个 PFFile 的函数怎么样...

    function fileFromUrl(url, name) {
        return Parse.Cloud.httpRequest({url: url}).then(function(httpResponse) {
            var imageBuffer = httpResponse.buffer;
            var base64 = imageBuffer.toString("base64");
            var file = new Parse.File(name, { base64: base64 });
            // edit - pretty sure file.save resolves to file, but just in case...
            return file.save().then(function() { return file; });
        }, function(error) {
            // can't fetch the image for some reason
            // ideas:
            return null; // and have your UI check for nulls in the user image array
            // or return cannedImage(); where this function returns a promise 
            // for a PFFile that already exists
        });
    }
    

    现在更容易编写一个为一组 url 执行此操作...

    var _ = require('underscore');
    function function filesFromUrls(urls) {
        var promises = _.map(urls, function (url, index) {
            return fileFromUrl(url, ''+index);
        });
        return Parse.Promise.when(promises).then(function() {
            return _.toArray(arguments);
        });
    }
    

    现在对用户来说更容易做到这一点......

    function convertUrlsToFilesForUser(user) {
        var profileURLs = user.get("profilePictures");
        return filesFromUrls(profileURLs).then(function(files) {
            // profileImages should be an array of PFFiles, not an array of arrays as you have it
            user.set("profileImages");
            return user.save();
        });
    }
    

    现在在云功能中很容易做到这一点...

    var userQuery = new Parse.Query("User");
    userQuery.find().then(function(users) {
        var promises = _.map(users, function(user) {
            return convertUrlsToFilesForUser(user);
        });
        return return Parse.Promise.when(promises);
    }).then(function(result) {
        response.success(result);
    }, function(error) {
        response.error(error);
    });
    

    注意事项:我没有测试此代码。这里所做的只是重组操作以简化和正确使用 Promise。在这些操作中,我使用了您的代码,也没有对其进行测试。

    我更改的一个设计元素是您似乎要保存数组数组profileImages,例如:[ [ PFFile, '0'], [ PFFile, '1'], ... 我把它改成了一个 PFFiles 数组。我认为您希望第二个元素像文件名。我在 PFFile 的构造函数中添加了它所属的名称(文件的名称并不重要,因为它只影响文件的半透明 url,无论如何解析都是唯一的)。

    最后一个警告。获取大量用户,然后获取大量图像,然后大量保存很容易超时。您可能需要在小批量用户中运行它以避免超时。

    【讨论】:

    • 谢谢danh,这最终奏效了。但我担心的一个问题是;如果 URL 损坏(过期),则 httpResponse 将返回为 text/plain。这意味着无法创建文件,因此无法保存。那么,有没有办法检查httpResponse.status == 403 的响应?
    • @ViktorGidlöf,在这个逻辑中,唯一会发现错误的地方是根。如果图像提取导致错误,则所有承诺都会落入response.error(),因此只有到该点处理的用户才会完成。但是 Promise 使得处理上下文中的错误变得容易。我制作了一个编辑功能来展示如何处理失败的 http 请求并继续进行。
    • 是的,这很好。我正在考虑创建一个您提到的名为cannedImage() 的函数,该函数将是一个新的httpRequest,我将从我知道有图像的URL 中获取图像?例如,我上传到自己的服务器的图像包含占位符图像。我会在错误函数中执行此操作还是如何创建占位符?谢谢!
    • 有几个选项,但我更喜欢将其保留为空。在大多数情况下,替换固定图像是 UI 的业务,它应该使用针对静态内容进行优化的单独架构来获得类似的东西。如果有充分的理由在云中执行此操作,则在将其句柄保留在新类中时获取文件并构建 PFFile。每次需要图像时重新获取并创建新的 PFFile 会非常浪费。
    • 是的,我想通了,它通过在错误句柄中返回 placeholderFile() 函数来工作,该函数又从未损坏的 URL 中返回占位符图像。现在我还有一个问题是我想从下载的文件中制作缩略图并将其设置为另一列。但也许这是另一个问题?
    猜你喜欢
    • 1970-01-01
    • 2016-03-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多