Can I write a data URI to an R2 bucket?

Is it possible to write to an R2 bucket with a base64 data URI i.e.
await env.r2_pub.put('some/file', 'data:image/png;base64,i5Gkw...');
await env.r2_pub.put('some/file', 'data:image/png;base64,i5Gkw...');
? That fails (it creates a text file with literally that content), but perhaps there's a way to encode the file contents first such that R2 accepts it?
4 Replies
Hello, I’m Allie!
Convert the base64 data to a Uint8Array
Mitya
Mitya3w ago
Thank you. You mean like this?
function base64ToArrayBuffer(base64) {
var binaryString = atob(base64);
var bytes = new Uint8Array(binaryString.length);
for (var i = 0; i < binaryString.length; i++) {
bytes[i] = binaryString.charCodeAt(i);
}
return bytes.buffer;
}
function base64ToArrayBuffer(base64) {
var binaryString = atob(base64);
var bytes = new Uint8Array(binaryString.length);
for (var i = 0; i < binaryString.length; i++) {
bytes[i] = binaryString.charCodeAt(i);
}
return bytes.buffer;
}
(Source)
kian
kian3w ago
You could probably use (await fetch('data:image/png;base64,i5Gkw...')).body, if it's any different performance-wise to that snippet is something you'd have to test.
Mitya
Mitya2w ago
Ah that's cool, never occurred to me to fetch a data URI. I'll try both and come back if any issues. Thanks! @kian's method was the only one that worked here. For reference in case anyone else finds this useful. Method 1: This produces InvalidCharacterError: atob() called with invalid...:
function base64ToArrayBuffer(base64) {
var binaryString = atob(base64);
var bytes = new Uint8Array(binaryString.length);
for (var i = 0; i < binaryString.length; i++) {
bytes[i] = binaryString.charCodeAt(i);
}
return bytes.buffer;
}
await env.r2_pub.put('some/key', base64ToArrayBuffer(myValidBase64String));
function base64ToArrayBuffer(base64) {
var binaryString = atob(base64);
var bytes = new Uint8Array(binaryString.length);
for (var i = 0; i < binaryString.length; i++) {
bytes[i] = binaryString.charCodeAt(i);
}
return bytes.buffer;
}
await env.r2_pub.put('some/key', base64ToArrayBuffer(myValidBase64String));
Method 2: (suggested by @kian) - this successfully saves the file to R2.
await env.r2_pub.put('some/key', (await fetch(myValidBase64String)).body);
await env.r2_pub.put('some/key', (await fetch(myValidBase64String)).body);
Method 3: (variant on method 1) - this SO answer suggested a different way to derive an array buffer that avoids atob(). This does save the file to R2, but with mangled source, such that it can't be opened.
var decodeBase64 = function(s) {
var e={},i,b=0,c,x,l=0,a,r='',w=String.fromCharCode,L=s.length;
var A="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
for(i=0;i<64;i++){e[A.charAt(i)]=i;}
for(x=0;x<L;x++){
c=e[s.charAt(x)];b=(b<<6)+c;l+=6;
while(l>=8){((a=(b>>>(l-=8))&0xff)||(x<(L-2)))&&(r+=w(a));}
}
return r;
};
var decodeBase64 = function(s) {
var e={},i,b=0,c,x,l=0,a,r='',w=String.fromCharCode,L=s.length;
var A="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
for(i=0;i<64;i++){e[A.charAt(i)]=i;}
for(x=0;x<L;x++){
c=e[s.charAt(x)];b=(b<<6)+c;l+=6;
while(l>=8){((a=(b>>>(l-=8))&0xff)||(x<(L-2)))&&(r+=w(a));}
}
return r;
};
On this generally, presumably passing a file as base64 to my back-end and saving it to R2 in this way isn't a terrible idea for any reason I'm missing?