Because you're restricting the solution to QML, your choices are severely limited. I would really question that since it leads to this solution, which is inefficient because it requires you to save the image first and then load the saved image.
You must:
- Initiate a save by calling
camera.imageCapture.capture() or equivalent
- Wait for the camera.imageCapture.onImageSaved signal
- This tells you that the image has been encoded and saved to a JPEG file
- You then can load the JPEG into memory as a binary ArrayBuffer object using
XMLHttpRequest
- Then, you can use
Qt.btoa on it to get a base64 string from it
The steps are precisely written above to avoid any mistakes or misunderstandings. It's inefficient, since, if you had access to C++ we could consider processing the raw QImage in-memory and do in-memory conversions to an image format of our choosing as well as an in-memory conversion to base64.
Camera {
id: camera
imageCapture {
onImageSaved: {
let filePath = camera.imageCapture.capturedImagePath;
console.log(filePath); // C:/Users/stephenquan/Pictures/IMG_00000007.jpg
let fileUrl = Qt.platform.os === 'windows'
? 'file:///' + filePath
: 'file://' + filePath;
console.log(fileUrl); // file:///C:/Users/stephenquan/Pictures/IMG_00000007.jpg
let xhr = new XMLHttpRequest();
xhr.open("GET", fileUrl, false);
xhr.send();
// //XMLHttpRequest: Using GET on a local file is dangerous and will be disabled by default in a future Qt version.Set QML_XHR_ALLOW_FILE_READ to 1 if you wish to continue using this feature.
let base64 = Qt.btoa(xhr.response);
console.log(base64); // final base64 representation of the JPEG image
let datauri = "data:image/jpeg;base64," + base64;
console.log(datauri); // a properly formatted datauri for the JPEG image
}
}
}
After it is saved, we need to convert that filePath to a fileUrl.
For me, as I'm running it on windows, I can add a file:/// prefix to turn it into a URL. For other platforms, the prefix is typically file://.
Then we can use Qt.btoa with XMLHttpRequest we can generate the base64 string for that URL.
See the warning about using XMLHttpRequest on local files.
Below is some refactored black box functions to make all this code reusable for other scenarios.
function urlToBase64(url) {
let xhr = new XMLHttpRequest();
xhr.open("GET", url, false);
xhr.send();
return Qt.btoa(xhr.response);
}
function fileToBase64(filePath) {
//XMLHttpRequest: Using GET on a local file is dangerous and will be disabled by default in a future Qt version.Set QML_XHR_ALLOW_FILE_READ to 1 if you wish to continue using this feature.
return urlToBase64(Qt.resolvedUrl("file:///" + filePath));
}
function test() {
let url = "https://www.google.com/images/branding/googlelogo/1x/googlelogo_color_272x92dp.png";
console.log(urlToBase64(fileUrl));
let filePath = "C:/temp/hello.txt";
console.log(fileToBase64(filePath));
}
[Edit: Additional answer about Canvas.toDataURL]
Alternatively, you can use Canvas.toDataURL but it requires you to load your graphic into the a Canvas object first and then render it once you are sure it's loaded.
[Edit: Additional answer about asynchronous]
There was a comment earlier regarding the proper usage of XMLHttpRequest. Because the use case was for local files, I used XMLHttpRequest.open with async mode set to false to force it to work in synchronous mode.
If you want an asynchronous version of the solution and use it generically for HTTP URLs, I would choose to use Promise chaining as per below.
function asyncUrlToBinary(url) {
return new Promise(function (resolve, reject) {
try {
let xhr = new XMLHttpRequest();
xhr.onreadystatechange = () => {
if (xhr.readyState !== 4) return;
if (xhr.status !== 200) {
reject(new Error(`HTTP Status Error ${xhr.status}: ${xhr.statusText}`));
return;
}
resolve(xhr.response);
}
xhr.open("GET", url);
xhr.send();
} catch (err) {
reject(err);
}
} );
}
function asyncBtoA(data) {
return Promise.resolve(Qt.btoa(data));
}
function asyncTest() {
let url = "https://www.google.com/images/branding/googlelogo/1x/googlelogo_color_272x92dp.png";
asyncUrlToBinary(url)
.then(data => asyncBtoA(data))
.then(base64 => console.log(base64))
.catch(err => { console.error(err.fileName + ":" + err.lineNumber, err.message); throw err; } )
;
}
References: