Display image from ID3 image data using javascript - javascript

I've tried all of the methods I could find in stackoverflow. This two are some of the most complete posts:
Display image from blob using javascript and websockets
How can you encode a string to Base64 in JavaScript?
I'm using cloudinary and id3js. First I upload the mp3 file to
cloudinary, then I request the file with Ajax through id3js. This
gives me all of the ID3 tags.
openUploadModal() {
cloudinary.openUploadWidget(window.cloudinaryOptions,
(errors, track) => {
if(!values(errors).length) {
id3(track[0].secure_url, (errs, tags) => {
this.setState({
title: tags.title,
audio_url: track[0].secure_url,
artist: tags.artist,
uploaded: true,
cover_photo: this.getImage(tags.v2.image)
});
});
}
});
}
And the image converter:
getImage(image) {
var arrayBuffer = image.data;
var bytes = new Uint8Array(arrayBuffer);
return "data:image/png;base64,"+btoa(unescape(encodeURIComponent(bytes)));
}
This is what the tags object looks like:
I then use the return value of getImage in the background-image attribute of a div. There are no errors in the console (not a bad request) but when opening the data:image/jpg;base64,... link there's only a little white square on the page.
How can I get a working url from the image object in the ID3 tags?

If image.data is an ArrayBuffer, you can use FileReader. FileReader load event is asynchronous, you cannot return the result from the function without using Promise, though you can use FileReaderSync() at Worker.
See also createImageBitmap alternative on Safari.
var reader = new FileReader();
reader.onload = function() {
// do stuff with `data URI` of `image.data`
console.log(reader.result);
}
reader.readAsDataURL(new Blob([image.data], {type:image.mime}));

Related

How to read remote image to a base64 data url

actually there are many answers for this question. But my problem is,
i want to generate pdf dynamically with 5 external(URL) images. Im using PDFmake node module.
it supports only two ways local and base64 format. But i don't want to store images locally.
so my requirement is one function which takes url as parameter and returns base64.
so that i can store in global variable and create pdfs
thanks in advance
function urlToBase(URL){
return base64;
}
var img = urlToBase('https://unsplash.com/photos/MVx3Y17umaE');
var dd = {
content: [
{
text: 'fjfajhal'
},
{
image: img,
}
]
};
var writeStream = fs.createWriteStream('myPdf.pdf');
var pdfDoc = printer.createPdfKitDocument(dd);
pdfDoc.pipe(writeStream);
pdfDoc.end();
im using PDFmake module from npm
The contents of the remote image can first be fetched with an HTTP request, for example using the ubiquitous request npm module. The image string contents can then be transformed to a buffer and finally converted to a base64 string. To complete the transformation, add the proper data-url prefix, for example, data:image/png,base64, to the beginning of the base64 string.
Here is a rough example for a PNG image:
const request = require('request-promise-native');
let jpgDataUrlPrefix = 'data:image/png;base64,';
let imageUrl = 'https://www.google.com/images/branding/googlelogo/1x/googlelogo_color_272x92dp.png';
request({
url: imageUrl,
method: 'GET',
encoding: null // This is actually important, or the image string will be encoded to the default encoding
})
.then(result => {
let imageBuffer = Buffer.from(result);
let imageBase64 = imageBuffer.toString('base64');
let imageDataUrl = jpgDataUrlPrefix+imageBase64;
console.log(imageDataUrl);
});

PreloadJS loaded image, but I cannot insert it to DOM

I'm trying to preload one image using PreloadJS, then insert it to DOM a twice. But I can insert only one image.
var queue = new createjs.LoadQueue();
queue.addEventListener('complete', onComplete);
queue.loadManifest([{
id: 'img',
type: 'image',
src: 'https://i.imgur.com/adSrF00g.png',
}, ]);
queue.load();
function onComplete(event) {
var img = queue.getResult('img');
var imgSrc = img.src;
// this code inserts image to the DOM but 404 not found error appears in console
document.getElementById('game').innerHTML = `<img src="${imgSrc}" />`;
// this code do the same thing and 404 also appears
var DOM_img = document.createElement("img");
DOM_img.src = imgSrc;
document.getElementById('game').appendChild(DOM_img);
// this code insert the image to the DOM succesfully
document.getElementById('game').appendChild(img);
// but the last line does nothing - the img tag does not appear in the DOM and nothing error in the console
document.getElementById('game').appendChild(img);
}
<script src="https://code.createjs.com/1.0.0/preloadjs.min.js"></script>
<div id="game"></div>
Screenshot: https://i.imgur.com/VQwAABM.jpg
I cannot understand why only one image appears. What I'am doing wrong? And how can I insert an image a twice? Thanks!
This is easy to fix, but first I want to explain you something.
What are Blob URLs or Object-URLs?
Blob URL/Object URL are a pseudo protocol to allow Blob and File objects to be used as URL source for things like images, download links for binary data and so forth.
In your case, If you try to open this src="blob:https://yada.yada" blob url that is the src of a loaded image it will give you an error and it can't be opened.
Yeah, I know that Teo, but Why if is working with the src tag how it is possible?
Well, Blob URLs can only be generated internally by the browser. So those elements have a special reference to the Blob or File object. These URLs can only be used locally in the single instance of the browser and in the same session (i.e. the life of the page/document).
So in this case the src="blob:https://yada.yada" is linked to the img tag created by the LoadQueue object.
Then, how can I use the same image in multiples elements?
In the LoadQueue docummentation that we can get a Blob object using the getResult() method. So, instead to reuse the Blob URL we can "clone" the Blob object. According to the documentation the getResult (value, [rawResult=false]) methods was overwritten to receive this rawResult optional parameter. If this parameter is true the method will returns a raw result as a Blob object, instead of a formatted result. If there is no raw result, the formatted result will be returned instead.
OK, but how can I use this blob in a <img> element?
Well, we can use URL.createObjectURL() method. This method creates a DOMString containing a new Blob URL representing the Blob object given in the parameter.
As I explained above, this Blob URL lifetime is tied to the document in the window on which it was created.
Here is the implementation:
let queue = new createjs.LoadQueue();
queue.addEventListener('complete', onComplete);
queue.loadManifest([{
id: 'sprite',
type: 'image',
src: 'https://i.imgur.com/ciIyRtu.jpg?1',
}, ]);
queue.load();
function onComplete(event) {
// Get Blob object instead of a formatted result
let blob = queue.getResult('sprite', true);
// Create a Blob URL using the Blob object
let urls = [
URL.createObjectURL(blob),
URL.createObjectURL(blob),
URL.createObjectURL(blob),
];
// Create a new <img> element and assign a Blob URL
urls.forEach(function(item, index, array) {
let DOM_img = document.createElement('img');
DOM_img.src = item;
document.getElementById('game').appendChild(DOM_img);
});
}
<script src="https://code.createjs.com/1.0.0/preloadjs.min.js"></script>
<div id="game"></div>
Alternative solution (Not recommended for big files)
Also you can use the loadManifest() to load the same image multiple times with different ids. Then we define a fileload event instead of complete event to capture each loaded element, check this example:
let queue = new createjs.LoadQueue();
queue.addEventListener('fileload', onFileLoad);
queue.loadManifest([{
id: 'sprite1',
type: 'image',
src: 'https://i.imgur.com/ciIyRtu.jpg?1',
}, {
id: 'sprite2',
type: 'image',
src: 'https://i.imgur.com/ciIyRtu.jpg?1',
}, {
id: 'sprite3',
type: 'image',
src: 'https://i.imgur.com/ciIyRtu.jpg?1',
}, ]);
function onFileLoad(event) {
// Get the image element after is successfully loaded
let img = event.result;
// This code insert the image to the DOM succesfully
document.getElementById('game').appendChild(img);
}
<script src="https://code.createjs.com/1.0.0/preloadjs.min.js"></script>
<div id="game"></div>
Just found another solution that loads image once and then insert it to dom a twice.
var queue = new createjs.LoadQueue();
queue.addEventListener('complete', onComplete);
queue.loadManifest([{
id: 'img',
type: 'image',
src: 'https://i.imgur.com/adSrF00g.png',
}, ]);
queue.load();
function onComplete(event) {
// get loading results as blob (second argument is set to true)
var blob = queue.getResult('img', true);
// create two blob urls from one blob image source
var blobUrl = URL.createObjectURL(blob);
var blobUrl2 = URL.createObjectURL(blob);
// create new IMG tag and set src as first blob url
var DOM_img = document.createElement("img");
DOM_img.src = blobUrl;
document.getElementById('game').appendChild(DOM_img);
// create new IMG tag and set src as second blob url
var DOM_img2 = document.createElement("img");
DOM_img2.src = blobUrl2;
document.getElementById('game').appendChild(DOM_img2);
}
<script src="https://code.createjs.com/1.0.0/preloadjs.min.js"></script>
<div id="game"></div>

What's DataTransferItemList and what is DataTransferItemList.add doing?

So I'm trying to understand paste and copy API in Google Chrome. I don't understand either.
As of copy, you'll probably want to use javascript to add something in clipboard. I'm working with images (strings work well actually1):
//Get DataTransferItemList
var files = items.items;
if(files) {
console.log(files);
//Create blob from canvas
var blob = Blob.fromDataURL(_this.editor.selection.getSelectedImage().toDataURL("image/png"));
var file;
try {
//Try to create file from blob, which may fail
file = new File([blob], "image.png", {type:"image/png"});
}
catch(e) {
return false;
}
if(file) {
//I think this should clear previous data from clipboard
files.clear();
//Add a file as image/png
files.add(file, "image/png");
}
//console.log(files.add(file));
}
The problem is, that I don't really know how does that add method work. I found this "documentation" for DataTransferItemList which says:
add(any data, optional DOMString type)
What's any data? (how can anybody even write this in documentation?) While something is added to clipboard, I don't know what it is. I can't paste it anywhere - except Chrome. If I inspect paste event with my copied file, this is in DataTransferItemList:
It can be converted to File, but if I try to turn it back to <img>:
ImageEditorKeyboard.prototype.processFile = function(file) {
//File reader converts files to something else
var reader = new FileReader();
//Refference to this class
var _this = this;
//happens when the file is loaded
reader.onload = function(event) {
var img = new Image;
img.onload = function() {
_this.processImage(this);
};
img.src = event.target.result;
}; // data url!
//Read file
reader.readAsDataURL(file);
}
I get the error2:
If I log the value of event.target.result it turns out that the data was empty:
console.error("String '", event.target.result, "' ain't a valid URL!");
Q: What is the exact specification of DataTransferItemList, it's methods and properties, especially the .add method?
1: To add string to clipboard (during copy event of course!), call this: event.clipboardData.setData(data, "text/plain");. The second argument doesn't seem to have any functionality - using image/png will not do anything.
2: Funny thing is that this error can't be caught.

Get Base64 encode file-data from Input Form

I've got a basic HTML form from which I can grab a bit of information that I'm examining in Firebug.
My only issues is that I'm trying to base64 encode the file data before it's sent to the server where it's required to be in that form to be saved to the database.
<input type="file" id="fileupload" />
And in Javascript+jQuery:
var file = $('#fileupload').attr("files")[0];
I have some operations based on available javascript: .getAsBinary(), .getAsText(), .getAsTextURL
However none of these return usable text that can be inserted as they contain unusable 'characters' - I don't want to have a 'postback' occur in my file uploaded, and I need to have multiple forms targeting specific objects so it's important I get the file and use Javascript this way.
How should I get the file in such a way that I can use one of the Javascript base64 encoders that are widely available!?
Thanks
Update - Starting bounty here, need cross-browser support!!!
Here is where I'm at:
<input type="file" id="fileuploadform" />
<script type="text/javascript">
var uploadformid = 'fileuploadform';
var uploadform = document.getElementById(uploadformid);
/* method to fetch and encode specific file here based on different browsers */
</script>
Couple of issues with cross browser support:
var file = $j(fileUpload.toString()).attr('files')[0];
fileBody = file.getAsDataURL(); // only would works in Firefox
Also, IE doesn't support:
var file = $j(fileUpload.toString()).attr('files')[0];
So I have to replace with:
var element = 'id';
var element = document.getElementById(id);
For IE Support.
This works in Firefox, Chrome and, Safari (but doesn't properly encode the file, or at least after it's been posted the file doesn't come out right)
var file = $j(fileUpload.toString()).attr('files')[0];
var encoded = Btoa(file);
Also,
file.readAsArrayBuffer()
Seems to be only supported in HTML5?
Lots of people suggested: http://www.webtoolkit.info/javascript-base64.html
But this only returns an error on the UTF_8 method before it base64 encodes? (or an empty string)
var encoded = Base64.encode(file);
It's entirely possible in browser-side javascript.
The easy way:
The readAsDataURL() method might already encode it as base64 for you. You'll probably need to strip out the beginning stuff (up to the first ,), but that's no biggie. This would take all the fun out though.
The hard way:
If you want to try it the hard way (or it doesn't work), look at readAsArrayBuffer(). This will give you a Uint8Array and you can use the method specified. This is probably only useful if you want to mess with the data itself, such as manipulating image data or doing other voodoo magic before you upload.
There are two methods:
Convert to string and use the built-in btoa or similar
I haven't tested all cases, but works for me- just get the char-codes
Convert directly from a Uint8Array to base64
I recently implemented tar in the browser. As part of that process, I made my own direct Uint8Array->base64 implementation. I don't think you'll need that, but it's here if you want to take a look; it's pretty neat.
What I do now:
The code for converting to string from a Uint8Array is pretty simple (where buf is a Uint8Array):
function uint8ToString(buf) {
var i, length, out = '';
for (i = 0, length = buf.length; i < length; i += 1) {
out += String.fromCharCode(buf[i]);
}
return out;
}
From there, just do:
var base64 = btoa(uint8ToString(yourUint8Array));
Base64 will now be a base64-encoded string, and it should upload just peachy. Try this if you want to double check before pushing:
window.open("data:application/octet-stream;base64," + base64);
This will download it as a file.
Other info:
To get the data as a Uint8Array, look at the MDN docs:
https://developer.mozilla.org/en/DOM/FileReader
My solution was use readAsBinaryString() and btoa() on its result.
uploadFileToServer(event) {
var file = event.srcElement.files[0];
console.log(file);
var reader = new FileReader();
reader.readAsBinaryString(file);
reader.onload = function() {
console.log(btoa(reader.result));
};
reader.onerror = function() {
console.log('there are some problems');
};
}
I used FileReader to display image on click of the file upload button not using any Ajax requests. Following is the code hope it might help some one.
$(document).ready(function($) {
$.extend( true, jQuery.fn, {
imagePreview: function( options ){
var defaults = {};
if( options ){
$.extend( true, defaults, options );
}
$.each( this, function(){
var $this = $( this );
$this.bind( 'change', function( evt ){
var files = evt.target.files; // FileList object
// Loop through the FileList and render image files as thumbnails.
for (var i = 0, f; f = files[i]; i++) {
// Only process image files.
if (!f.type.match('image.*')) {
continue;
}
var reader = new FileReader();
// Closure to capture the file information.
reader.onload = (function(theFile) {
return function(e) {
// Render thumbnail.
$('#imageURL').attr('src',e.target.result);
};
})(f);
// Read in the image file as a data URL.
reader.readAsDataURL(f);
}
});
});
}
});
$( '#fileinput' ).imagePreview();
});
Inspired by #Josef's answer:
const fileToBase64 = async (file) =>
new Promise((resolve, reject) => {
const reader = new FileReader()
reader.readAsDataURL(file)
reader.onload = () => resolve(reader.result)
reader.onerror = (e) => reject(e)
})
const file = event.srcElement.files[0];
const imageStr = await fileToBase64(file)
Complete example
Html file input
<style>
.upload-button {
background-color: grey;
}
.upload-button input{
display:none;
}
</style>
<label for="upload-photo" class="upload-button">
Upload file
<input
type="file"
id="upload-photo"
</input>
</label>
JS Handler
document.getElementById("upload-photo").addEventListener("change", function({target}){
if (target.files && target.files.length) {
try {
const uploadedImageBase64 = await convertFileToBase64(target.files[0]);
//do something with above data string
} catch() {
//handle error
}
}
})
function convertFileToBase64(file) {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.readAsDataURL(file);
reader.onload = () => resolve(reader.result);
// Typescript users: use following line
// reader.onload = () => resolve(reader.result as string);
reader.onerror = reject;
});
}
After struggling with this myself, I've come to implement FileReader for browsers that support it (Chrome, Firefox and the as-yet unreleased Safari 6), and a PHP script that echos back POSTed file data as Base64-encoded data for the other browsers.
So why dont you agree with user of the system to select an image from a known folder? Or they can set their choice folder for images.
Most browsers wont support full path but you can get the filename eg "image.png"
Using PHP inbuilt function to encode:
#$picture_base64 = base64_encode( file_get_contents($image_file_name) );
The sign # will suppress error if path is not found but the result will be a null for variable $picture_base64 so i guess youre ok with null like i am else do a check for null before proceeding.
In html you can select an image filename to the input e.g. "image.png" ( but not the full path)
<input type="file" name="image" id="image" >
Then in PHP you can do:
$path = "C:\\users\\john\\Desktop\\images\\"
#$picture_base64 = base64_encode( file_get_contents( $path. $_POST['image']);
Then $picture_base64 will be something like
"AQAAAAMAAAAHAAAADwAAAB8AAAA/AAAAfwAAAP8AAAD/AQAA/w"
I've started to think that using the 'iframe' for Ajax style upload might be a much better choice for my situation until HTML5 comes full circle and I don't have to support legacy browsers in my app!

Is it possible to save a File object in LocalStorage and then reload a File via FileReader when a user comes back to a page?

For example, say the user loads some very large images or media files in to your web app. When they return you want your app to show what they've previously loaded, but can't keep the actual file data in LocalStorage because the data is too large.
This is NOT possible with localStorage. Data stored in localStorage needs to be one of the primitive types that can be serializable. This does not include the File object.
For example, this will not work as you'd expect:
var el = document.createElement('input');
el.type='file';
el.onchange = function(e) {
localStorage.file = JSON.stringify(this.files[0]);
// LATER ON...
var reader = new FileReader();
reader.onload = function(e) {
var result = this.result; // never reaches here.
};
reader.readAsText(JSON.parse(localStorage.f));
};
document.body.appendChild(el);
The solution is to use a more powerful storage option like writing the file contents to the HTML5 Filesystem or stashing it in IndexedDB.
Technically you can if you just need to save small files in localStorage.
Just base64 that ish and since it's a string... it's localStorage-friendly.
I think localStorage has a ~5MB limit. base64 strings are pretty low file size so this is a feasible way to store small images. If you use this lazy man's way, the downside is you'll have to mind the 5MB limit. I think it could def be a solution depending on your needs.
Yes, this is possible. You can insert whatever information about the file you want into LocalStorage, provided you serialize it to one of the primitive types supported. You can also serialize the whole file into LocalStorage and retrieve that later if you want, but there are limitations on the size of the file depending on browser.
The following shows how to achieve this using two different approaches:
(function () {
// localStorage with image
var storageFiles = JSON.parse(localStorage.getItem("storageFiles")) || {},
elephant = document.getElementById("elephant"),
storageFilesDate = storageFiles.date,
date = new Date(),
todaysDate = (date.getMonth() + 1).toString() + date.getDate().toString();
// Compare date and create localStorage if it's not existing/too old
if (typeof storageFilesDate === "undefined" || storageFilesDate < todaysDate) {
// Take action when the image has loaded
elephant.addEventListener("load", function () {
var imgCanvas = document.createElement("canvas"),
imgContext = imgCanvas.getContext("2d");
// Make sure canvas is as big as the picture
imgCanvas.width = elephant.width;
imgCanvas.height = elephant.height;
// Draw image into canvas element
imgContext.drawImage(elephant, 0, 0, elephant.width, elephant.height);
// Save image as a data URL
storageFiles.elephant = imgCanvas.toDataURL("image/png");
// Set date for localStorage
storageFiles.date = todaysDate;
// Save as JSON in localStorage
try {
localStorage.setItem("storageFiles", JSON.stringify(storageFiles));
}
catch (e) {
console.log("Storage failed: " + e);
}
}, false);
// Set initial image src
elephant.setAttribute("src", "elephant.png");
}
else {
// Use image from localStorage
elephant.setAttribute("src", storageFiles.elephant);
}
// Getting a file through XMLHttpRequest as an arraybuffer and creating a Blob
var rhinoStorage = localStorage.getItem("rhino"),
rhino = document.getElementById("rhino");
if (rhinoStorage) {
// Reuse existing Data URL from localStorage
rhino.setAttribute("src", rhinoStorage);
}
else {
// Create XHR, BlobBuilder and FileReader objects
var xhr = new XMLHttpRequest(),
blob,
fileReader = new FileReader();
xhr.open("GET", "rhino.png", true);
// Set the responseType to arraybuffer. "blob" is an option too, rendering BlobBuilder unnecessary, but the support for "blob" is not widespread enough yet
xhr.responseType = "arraybuffer";
xhr.addEventListener("load", function () {
if (xhr.status === 200) {
// Create a blob from the response
blob = new Blob([xhr.response], {type: "image/png"});
// onload needed since Google Chrome doesn't support addEventListener for FileReader
fileReader.onload = function (evt) {
// Read out file contents as a Data URL
var result = evt.target.result;
// Set image src to Data URL
rhino.setAttribute("src", result);
// Store Data URL in localStorage
try {
localStorage.setItem("rhino", result);
}
catch (e) {
console.log("Storage failed: " + e);
}
};
// Load blob as Data URL
fileReader.readAsDataURL(blob);
}
}, false);
// Send XHR
xhr.send();
}
})();
Source

Categories

Resources