diff --git a/CHANGELOG.md b/CHANGELOG.md index 09b9ecd..431778a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ * End requests with an error when parsing posted body contents fail * Make the max body & file size of a request configurable (per route) +* Add support for downloading `data:` uris with `Alchemy#download()` ## 1.4.0-alpha.6 (2024-09-11) diff --git a/lib/core/alchemy_functions.js b/lib/core/alchemy_functions.js index b80b251..b3b3b6e 100644 --- a/lib/core/alchemy_functions.js +++ b/lib/core/alchemy_functions.js @@ -1,6 +1,7 @@ let mkdirp = alchemy.use('mkdirp')?.mkdirp, ncp = alchemy.use('ncp').ncp, fs = alchemy.use('fs'), + fsp = fs.promises, libpath = alchemy.use('path'), child = alchemy.use('child_process'), crypto = alchemy.use('crypto'), @@ -1106,6 +1107,53 @@ Alchemy.setMethod(function request(url, options, callback) { return Blast.fetch(options, callback); }); +/** + * Turn a `data:` uri into a file + * + * @author Jelle De Loecker + * @since 1.4.0 + * @version 1.4.0 + * + * @param {string} data_uri + * + * @return {Pledge} + */ +function convertDataUriToFile(data_uri) { + + // Split the data uri into its 2 parts + let pieces = data_uri.slice(5).split(','); + + // Split the info bit + let info = pieces[0].split(';'); + + // Get the expected mime type + let mime_type = info[0]; + + // And is it base64? + let is_b64 = info[1] && info[1].toLowerCase() == 'base64'; + + let buffer; + + if (is_b64) { + buffer = Buffer.from(pieces[1], 'base64'); + } else { + buffer = Buffer.from(String.decodeURI(pieces[1])); + } + + return Pledge.Swift.waterfall( + () => Blast.createTempDir({prefix: 'aldl'}), + async (temp_dir) => { + let full_path = libpath.resolve(temp_dir, 'buffer_' + alchemy.ObjectId()); + + await fsp.writeFile(full_path, buffer); + + return full_path; + }, + (full_path) => Classes.Alchemy.Inode.Inode.from(full_path) + ); + +} + /** * Download a file * @@ -1119,6 +1167,14 @@ Alchemy.setMethod(function request(url, options, callback) { */ Alchemy.setMethod(function download(url, options) { + if (url.startsWith('data:')) { + try { + return convertDataUriToFile(url, options); + } catch (err) { + return Pledge.reject(err); + } + } + const pledge = new Pledge(); // Get the file