I want to use server-to-server cloudkit js. to save record with Asset field.
the Asset field is a m4a audio. after saved, the audio file is corrupt to play
The Apple's Doc is not clear about the Asset field.
In a record that is being saved to the database, the value of an Asset field must be a window.Blob type. In the code fragment above, the type of the assetFile variable is window.File.
Docs: https://developer.apple.com/documentation/cloudkitjs/cloudkit/database/1628735-saverecords
but in nodejs ,there is no Blob or .File, I filled it with a buffer like this code:
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);
}
});
The record is successful be saved.
But when I download the audio from icloud.developer.apple.com/dashboard .
the audio file is corrupt to play.
What's wrong with it. thank you to reply.
I was having the same problem and have found a working solution!
Remembering that CloudKitJS needs you to define your own
fetch
method, I implemented a custom one to see what was going on. I then attached a debugger on the customfetch
to inspect the data that was passing through it.After stepping through the caller, I found that all asset values are transformed using its
toString()
method only when the library is embedded in NodeJS. This is determined by the absence of the globalwindow
object.When
toString()
is called on aBuffer
, its contents are encoded to UTF-8 (by default), which causes binary assets to become malformed. If you're usingnode-fetch
for yourfetch
implementation, it supportsBuffer
andstream.Readable
, so thistoString()
call does nothing but harm.The most unobtrusive fix I've found is to swap the
toString()
method on anyBuffer
orstream.Readable
instances passed as an asset field values. You should probably usestream.Readable
, by the way, so that you don't load the entire asset in memory when uploading.Anyway, here's what it looks like in practice:
Please be aware that this workaround mutates a
Buffer
in an ugly way (sinceBuffer
apparently can't be extended). It's probably a good idea to design an API which doesn't useBuffer
arguments so that you can mutate instances that only you create yourself to avoid unintended side effects anywhere else in your code.Also, sure to vendor (make a local copy) of CloudKitJS in your project, as the behavior may change in the future.
ORIGINAL ANSWER
I ran into the same problem and solved it by encoding my data using Base64. It appears that there's a bug in their SDK which mangles
Buffer
instances containing non-ascii characters (which, um, seems problematic).Anyway, try something like this:
Side note:
You'll need to decode the asset(s) on the device before using them. There's no way to do this efficiently without writing your own routines, as the methods included in
Data
/NSData
instances requires all data to be in memory.This is a problem with CloudKitJS (and not the native CloudKit client / service), so the other option is to write your own routine to upload assets.
Neither of these options seem particularly great, but rolling your own atleast means there aren't extra steps for clients to take in order to use the asset.