From 6837c964284da00edf8515c3b5691e989d5bae45 Mon Sep 17 00:00:00 2001 From: Jelle De Loecker Date: Thu, 10 Oct 2024 20:10:13 +0200 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20Add=20support=20for=20downloading?= =?UTF-8?q?=20`data:`=20uris=20with=20`Alchemy#download()`?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 1 + lib/core/alchemy_functions.js | 56 +++++++++++++++++++++++++++++++++++ 2 files changed, 57 insertions(+) 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