2

我想使用服务器到服务器的 cloudkit js。用资产字段保存记录。
Asset 字段是一个 m4a 音频。保存后,音频文件损坏无法播放

Apple's Doc 不清楚 Asset 字段。
在保存到数据库的记录中,Asset 字段的值必须是 window.Blob 类型。在上面的代码片段中,assetFile 变量的类型是 window.File。
文档: https ://developer.apple.com/documentation/cloudkitjs/cloudkit/database/1628735-saverecords

但是在 nodejs 中,没有Blob.File,我用这样的代码填充了它:


var dstFile = path.join(__dirname,"../test.m4a");
var data = fs.readFileSync(dstFile);
let buffer = Buffer.from(data);

var rec = {
    recordType: "MyAttachment",
    fields: {
      ext: { value: ".m4a" },
      file: { value: buffer }
    }
  }
  //console.debug(rec);
  mydatabase.newRecordsBatch().create(rec).commit().then(function (response) {
    if (response.hasErrors) {
      console.log(">>> saveAttachFile record failed");
      console.warn(response.errors[0]);

    } else {
      var createdRecord = response.records[0];
      console.log(">>> saveAttachFile record success:", createdRecord);
    }
  });

记录保存成功。 成功 但是当我从 icloud.developer.apple.com/dashboard 下载音频时。
音频文件已损坏,无法播放
它出什么问题了。谢谢你的回复。

4

1 回答 1

2

我遇到了同样的问题并找到了可行的解决方案!

记住 CloudKitJS 需要您定义自己的fetch方法,我实现了一个自定义方法来查看发生了什么。然后我在自定义上附加了一个调试器fetch来检查通过它的数据。

在单步执行调用者之后,我发现只有当库嵌入到 NodeJS 中时,所有资产值都会使用它的toString()方法进行转换。这是由缺少全局对象决定的。window

toString()a上调​​用时Buffer,其内容被编码为 UTF-8(默认情况下),这会导致二进制资产格式错误。如果你正在使用node-fetch你的fetch实现,它支持Bufferand stream.Readable,所以这个toString()调用只会造成伤害。

我发现的最不显眼的解决方法是在作为资产字段值传递的toString()任何Buffer或实例上交换方法。stream.Readable顺便说一句,您可能应该使用stream.Readable,以便在上传时不会将整个资产加载到内存中。

无论如何,这就是它在实践中的样子:

// Put this somewhere in your implementation
const swizzleBuffer = (buffer) => {
    buffer.toString = () => buffer;
    return buffer;
};

// Use this asset value instead
{ asset: swizzleBuffer(fs.readFileSync(path)) }

请注意,这种解决方法会Buffer以一种丑陋的方式改变 a(因为Buffer显然无法扩展)。设计一个不使用Buffer参数的 API 可能是个好主意,这样您就可以改变只有您自己创建的实例,以避免代码中其他任何地方的意外副作用。

此外,请确保在您的项目中供应商(制作本地副本)CloudKitJS,因为将来行为可能会发生变化。

原始答案

我遇到了同样的问题,并通过使用 Base64 编码我的数据来解决它。他们的 SDK 中似乎存在一个错误,该错误会破坏Buffer包含非 ascii 字符的实例(嗯,这似乎有问题)。

无论如何,尝试这样的事情:

const assetField = { value: Buffer.from(data.toString('base64')), 'ascii') }

旁注

在使用它们之前,您需要对设备上的资产进行解码。如果不编写自己的例程,就无法有效地做到这一点,因为Data/ NSDatainstances 中包含的方法要求所有数据都在内存中。

这是 CloudKitJS(而不是原生 CloudKit 客户端/服务)的问题,因此另一种选择是编写自己的例程来上传资产。

这些选项似乎都不是特别好,但至少滚动您自己的选项意味着客户无需采取额外步骤即可使用该资产。

于 2019-11-10T15:17:40.157 回答