JSON Schema data-url file upload

I want to upload a file to a RESTish API. I define the upload form using the "JSON Schema" component. My JSON schema for the file input field looks like this:

"file": {
    "type": "string",
    "format": "data-url",
    "title": "Document file"
}

I then try to upload the file with the "RESTQuery resource". The input file is used there as a "Form Data File" like so:

{{Form.data.file}}

But when I test the upload I see that actually almost nothing gets uploaded.

When I use the "File Dropzone" component instead I can successfully upload the file. On close inspection I see that the "File Dropzone" component exposes the file information in a different way than the "JSON Schema" component:

"File Dropzone":
Screenshot 2024-02-12 at 16.34.05

"JSON Schema":
Screenshot 2024-02-12 at 16.35.16

Any idea how to deal with this?

Thanks!

it looks like an empty file is being uploaded.

{{ Caregiver_Upload_form2.data.file }} needs to have the base64Data property

{
name: string,
type: string,
sizeBytes: number,
base64Data: "data:image/png;base64," + base64string,
}

Thanks for the reply @bobthebear

Thats the thing. When using the regular "File Dropzone" component, the file object value is already structured in the way you described (object with 4 keys, including base64Data). When using the "data-url" property in the "JSON Schema Form" I only get a single string where everything (file name, type, base64data) is concatenated and that looks rather hard to parse / break down again.

So the question is probably: what exactly does the "RESTQuery resource" expect / accept for a body form data file?

if it's in the form "data:mime/type;name=something.ext;base64,really_long_string"

let encodedUrl = "data:mime/type;name=something.ext;base64,really_long_string"
// split into
// 0) data:mime/type;name=something.ext
// 1) really_long_string
let encodedSplit = encodedUrl.split(';base64,');
let base64string = encodedSplit[1];
// data:mime/type;name=something.ext
// split into
// 0) data:mime/type
// 1) something.ext
let nameTypeSplit = encodedSplit[0].split(';name=');
let name = encodedUrl.slice(encodedUrl. nameTypeSplit[1];
let type = nameTypeSplit[0].split(':')[1];

//we can check if the name has the file extension and add it if we want
let fileExtension = '.' + type.split('/')[1];
name += name.endsWith(fileExtension) ? "" : fileExtension;

//the formula for base64string to filesize in bytes is:
// filesize = base64string.replaceAll('=', '').length * (3 / 4) //base64string.length * (3 / 4)) 
//    these strings are padded with '=' which bloats the filesize by a bit throwing off the math (from rounding/truncating).  
//    so to get a more precise number we remove any padding then do the math.
1 Like

just noticed yours is a bit different. fixed it to match the screenshot you provided. that will give you the name, type, size and you already had the base64string so you now have all the parts to make the object you know it accepts:

let encodedSplit = encodedUrl.split(';base64,');
let base64string = encodedSplit[1];
let nameTypeSplit = encodedSplit[0].split(';name=');
let name = encodedUrl.slice(encodedUrl. nameTypeSplit[1];
let ftype = nameTypeSplit[0].split(':')[1];
let myObj = {
  name: name ,
  type: ftype ,
  sizeBytes: base64string.replaceAll('=', '').length * (3 / 4),
  base64Data: encodedUrl,
}

myRestQuery.trigger({
  additionalScope: {
    fileObj: myObj
  }
}

then in your query you can use {{ fileObj }} for the request body (json). the linter might claim fileObj is an error, but if you run it there shouldn't be any problems (the linter doesn't know about anything in additionalScope since it doesn't exist until you call .trigger() so it gets confused and gives it a red squiggly line). there's other ways you can do that obv, like set a variable to the obj you made then use it in a query

1 Like

@bobthebear brilliant, thanks so much! This will be very helpful!

A bit disappointed in retool why we need to jump through all these hoops. Maybe someone from the @retool-team can have a look if this should be the intended behaviour of the JSON-Schema component when using data-url.

@joeBumbaca @victoria @himanshu :eyes: