I would like to know how to get the size in bytes from the "src" from an "img" tag with HTML/JS.
<img src="https://ofbuckleyandbeatles.files.wordpress.com/2011/01/testpattern.gif"/>
In the above example I would basicly want to know how big "testpattern.gif" is (in bytes).
Thanks in advance.
Well, this is 2017 and you can now use Resource Timing API to extract an image's transferSize, encodedBodySize, decodedBodySize within javascript:
Below is the code to access size information for all the imgs on a page (see the caveat to all below):
var imgElems = document.getElementsByTagName('img');
for ( var i=0, len = imgElems.length; i < len; i++ )
{
var url = imgElems[i].src || imgElems[i].href;
if (url && url.length > 0)
{
var iTime = performance.getEntriesByName(url)[0];
console.log(iTime.transferSize); //or encodedBodySize, decodedBodySize
}
}
Make sure you run the above code after document onload (to be sure images are loaded)
Due to CORS, timing information for images coming from a different domain may not be available (unless the different domain's server has set Timing-Allow-Origin HTTP response header)
Be sure resource timing api is available for the browser(s) you are targetting : http://caniuse.com/#feat=resource-timing
Make sure you get a grasp of the difference between transferSize, encodedBodySize, decodedBodySize to be sure you use the right size attribute.
Sadly the proposed solution does not work since the returned value does not match the image actual size.
Assuming you are using a URL you can do the following:
const fileImg = await fetch(URL_TO_IMG).then(r => r.blob());
This will get the resource through HTTP and then returns the full binary as a blob object, then you can access its properties including its size in bytes as:
fileImg.size
Related
I've gotten this IPFS info such as "/ipfs://QmQqzMTavQgT4f4T5v6PWBp7XNKtoPmC9jvn12WPT3gkSE" as API response.
I want to display this file(image) on my page, but I can't find out the correct solution.
How can I get the image URL from this info in react app?
Please help with my concern.
Try adding https://ipfs.io in the beginning of your ipfs info as suggested in this document https://docs.ipfs.io/concepts/what-is-ipfs/
i.e
ipfs://QmQqzMTavQgT4f4T5v6PWBp7XNKtoPmC9jvn12WPT3gkSE
becomes
https://ipfs.io/ipfs/QmQqzMTavQgT4f4T5v6PWBp7XNKtoPmC9jvn12WPT3gkSE
If you're using js-ipfs, you can retrieve the image over IPFS, and display it:
/** Uses `URL.createObjectURL` free returned ObjectURL with `URL.RevokeObjectURL` when done with it.
*
* #param {string} cid CID you want to retrieve
* #param {string} mime mimetype of image
* #param {number} limit size limit of image in bytes
* #returns ObjectURL
*/
async function loadImgURL(cid, mime, limit) {
if (cid == "" || cid == null || cid == undefined) {
return;
}
const content = [];
for await (const chunk of ipfs.cat(cid, {length:limit})) {
content.push(chunk);
}
return URL.createObjectURL(new Blob(content, {type: mime}));
}
Then you can display it with something like:
<body>
<img id="myImage" />
<script>
async function setImage() {
// just an example, make sure to free the resulting ObjectURL when you're done with it
//
// if your CID doesn't work, try this one: Qmcm32sVsMYhURY3gqH7vSQ76492t5Rfxb3vsWCb35gVme
// that's a popular CID, which should resolve every time
document.getElementById("myImage").src = await loadImgURL("QmQqzMTavQgT4f4T5v6PWBp7XNKtoPmC9jvn12WPT3gkSE", "image/png", 524288);
}
setImage();
</script>
</body>
The big advantage of this is you're using the IPFS network itself, and not relying on a public HTTP gateway (the recommended way).
You can do something like that:
tokenURI.replace("ipfs://", "https://ipfs.io/ipfs/");
One thing to note here about fetching images from IPFS that I think isn't being discussed sufficiently in these answers is that you will need to either
Run your own node of IPFS, or
Get a hosted IPFS node through a service like Infura
I have spent a little bit of time working through this in the last couple of days, and it will always come back to you having to have direct access to your own node.
There are "Gateways," which are nodes hosted by the community, and you can read more about them in the IPFS docs here: https://docs.ipfs.io/concepts/ipfs-gateway/#limitations-and-potential-workarounds
The thing with the gateways is that they are not meant to be relied upon for production sites, as you can see below. It's possible that there is some hosted node out there that somebody is giving out for free, but I doubt it, and you wouldn't want to rely on that anyways.
I think that other questions above have elaborated how you actually process the responses once you get it, but I wanted to cover this extra ground in my answer.
ipfs docs
let imgUrl = url?.slice(url.indexOf(":"),url?.lastIndexOf("/"));
let slice = url?.slice(url.lastIndexOf("/"),url?.length)
let renderURL = `https${imgUrl}.ipfs.dweb.link${slice}`;
console.log(renderURL);
I found out that you can have a fallback URL for an <img> using onerror if the first loading fails.
But it is possible to provide a list of sources (urls) to try and keep trying until one of them loads?
I was messing around with this for about an hour and was working towards this solution with JavaScript. I think its very clunky and I wonder if there is more more pretty way of doing this.
My ugly approach
Each image has several data attributes such as fallback1, fallback2 with the alternatives sources to load from if loading fails. Every image also has this.onerror=null;this.src='error.jpg
So every image that fails to load will show the picture error.jpg instead.
So an image may look like this
<img src="https://i.imgur.com/iYNdJeW.jpg" onerror="this.onerror=null;this.src='error.jpg'" data-fallback1="https://i.imgur.com/iYNdJeW2.jpg" data-fallback2="https://i.imgur.com/iYNdJeW3.jpg" />
Call a script in window.onload that iterates over each image and check if the source ends with error.jpg.
If it does it takes the first fallback url in that image's data properties (fallback1) and changes the source to that. Then removes that data-property from the image (not sure if that is possible or meant to be done), because he has already been tried. Then the script recursively runs again.
The recursion stops if if no images' source ends in error.jpg OR If all images whose source ends with error.jpg do not have any data attributes (no more alternative sources to try).
I think it would work, but it seem very very hackish.
I found out about <picture> and <source> and was very optimistic, but <picture> just accepts a 404 if the image does not load.
Has anyone come up with a better approach for doing this? Ideally I wish you could give img a list of urls and it would just keep trying until it got a non-error.
You're setting onerror to null when your first error is fired. Onerror will automatically fire if you change the source and it fails to load again. You could just store the list of fallbacks and increment an index each time onerror is fired.
Here's an example, and you could easily convert this to store all of the different variables directly on the element. I've provided 4 fake URLs and a final placeholder image as a real URL. You'll see it does load the placeholder image.
var fallbacks = ["https://example.com/badurl1.jpg", "https://example.com/badurl2.jpg", "https://example.com/badurl3.jpg", "https://via.placeholder.com/350x150"];
var index = 0;
document.querySelector("img").onerror = function(){
if(index >= fallbacks.length) return;
let next = fallbacks[index];
this.src = next;
index++;
};
<img src="https://example.com/badurl.jpg"/>
This will cycle through a list until your image stops getting an error
to edit it, change the 'fallbacksrc' array
HTML
<img src="*" id="img" onerror="ImgOnError()"></img>
JS
let fallbacksrc=[]; //All All Fallbacks Here, when error will start at the second one because 1st one is already tried
let fallbackcount = 1;
let Img = document.getElementById('img')
function ImgOnError(){
if(fallbackcount >= fallbacksrc.length){
fallbackcount = 0
Img.src=fallbacksrc[fallbackcount]
}else{
Img.src=fallbacksrc[fallbackcount]
}
fallbackcount++;
}
I've come up with a vanilla-JavaScript way where you don't need to specify the image in javascript.
Define this function:
function incrementFallbackSrc(img, srcs) {
if (typeof img.fallbackSrcIndex === 'undefined') img.fallbackSrcIndex = 0;
img.src = srcs[img.fallbackSrcIndex++];
}
and for each image, pass all the fallback URLs in the function call.
<img
src="https://i.imgur.com/iYNdJeW.jpg"
onerror="javascript: incrementFallbackSrc(this, ['https://i.imgur.com/iYNdJeW2.jpg',
'https://i.imgur.com/iYNdJeW3.jpg',
'error.jpg'])">
>
to set it programmatically, you may have to put the function inside the definitiaion like so:
img.setAttribute('onerror', "function incrementFallbackSrc(img, srcs) {if (typeof img.fallbackSrcIndex === 'undefined') img.fallbackSrcIndex = 0;img.src = srcs[img.fallbackSrcIndex++];}; incrementFallbackSrc(this, ['" + fallbackUrl1 + "," + fallbackUrl2 + "'])");
I have a web application that is pulling in an RSS feed from an external resource. Some of the images being pulled in from the feed are relative paths such as /assets/images/image.jpg. The images with relative paths end up being 404's because they do not call back to the external resource for it to be an absolute path like externalsource.com/assets/images/image.jpg. I want to write a javascript function that collects all of the img src tags on the page and ensures that they are all absolute paths. I have no problem collecting all of the img srcs but cannot replace them with the absolute url. When printing out the img srcs, the url from the resource that is pulling in the feed is applied to the url. Like resource/assets/images/image.jpg
Here is what I have thus far:
var images = document.getElementsByTagName('img');
var srcList = [];
for(var i = 0; i < images.length; i++) {
var pattern = new RegExp('^https?:\/\/fiddle\.jshell\.net(\/)');
if (pattern.test(images[i].src)){
var sources = images[i].src;
images[i].src = sources.replace("^https?:\/\/fiddle\.jshell\.net(\/)", "http://duo.com");
console.log(images[i].src);
}else{
console.log("no match");
}
}
I am collecting all of the srcs, comparing them to a regex to see if it matches the resource url, and replacing it with the url from the external resource. Unfortunately, the replacing the url portion is not working whatsoever. It needs to be pure javascript and no jquery.
Any help is really appreciated!
It looks like you passed a string to the first argument of String#replace:
images[i].src = sources.replace("^https?:\/\/fiddle\.jshell\.net(\/)", "http://duo.com")
You probably meant to pass a regular expression (note the use of / rather than "; this represents a regex literal in JS):
images[i].src = sources.replace(/^https?:\/\/fiddle\.jshell\.net(\/)/, "http://duo.com")
However, I would advise that the majority of your looping and testing logic be reduced to a single call to replace within a forEach loop, like so:
[].forEach.call(document.getElementsByTagName('img'), function (image) {
image.src = image.src.replace(/^https?:\/\/fiddle\.jshell\.net\//, 'http://duo.com/')
console.log(image.src)
})
<img src="http://fiddle.jshell.net/favicon.ico">
I am trying to capture a still frame from an (any) external swf file, by using my own flash movie as a proxy to load it and hand information regarding the Stage onto javascript. I want to keep it as wide compatible as possible, so I went with AS2 / Flash 8 for now.
The script works fine in the Flash debugger, i.e. the
trace(flash2canvasScreenshot.getPixel(w, h).toString(16));
returns the correct pixel color, where as:
ExternalInterface.call("sendToJS",flash2canvasScreenshot.getPixel(w, h).toString(16));
in the published movie doesn't.
This method can obviously be quite slow for large flash (dimension wise) movies, as it iterates every single pixel. If someone has any better methods in mind, feel free to share, but as said, the problem I am facing is that I am getting differentiating results in debugging and publishing, with the pixel information not getting fetched when published.
import flash.display.BitmapData;
import flash.external.*;
var myLoader:MovieClipLoader = new MovieClipLoader();
var mclListener:Object = new Object();
mclListener.onLoadInit = function(target_mc:MovieClip)
{
var stageW = Stage.width;
var flash2canvasScreenshot:BitmapData = new BitmapData(stageW, Stage.height, false, 0x00000000);
var pixels:Array = new Array();
flash2canvasScreenshot.draw(element);
for (w = 0; w <= stageW; w++)
{
trace(flash2canvasScreenshot.getPixel(w, h).toString(16)); // this gives correct color value for the pixels in the debugger
ExternalInterface.call("sendToJS",flash2canvasScreenshot.getPixel(w, h).toString(16)); // this just returns the bitmap default color, 0 in this case.
/*
for (h = 0; h <= Stage.height; h++)
{
var pixel = flash2canvasScreenshot.getPixel(w, h).toString(16);
pixels.push(pixel);
}
*/
}
//ExternalInterface.call("sendToJS",pixels.toString());*/
};
myLoader.addListener(mclListener);
myLoader.loadClip("http://i.cdn.turner.com/cnn/cnnintl_adspaces/2.0/creatives/2010/6/9/21017300x250-03.swf", 0);
//myLoader.loadClip("https://s.ytimg.com/yt/swfbin/watch_as3-vflJjAza6.swf", 0);
//myLoader.loadClip(_level0.flash2canvasurl, _root.mc);
There are few problems with the snippet you posted:
like the one Joey mentioned, but the one that stands out from my
point of view is the element variable which isn't defined
anywhere, so that either is a type o, or you're trying to draw an
undefined object.
You're drawing as soon as the load is finished, but the animation you're loading might start slightly later. Maybe take the snapshot a bit after the load is complete.
Haven't touched as2 for some time and don't remember how security issue are handled, but if you're swf is loading another swf from a different domain, then the domain hosting the swf you're loading should also have a crossdomain.xml policy file allowing you to access the content of the loaded swf. If you simply load and display a swf from another domain, that's fine. However, if you're trying to draw the swf using BitmapData, you're actually attempting to access pixel data from the content of that swf, therefore you would need permissions. If you have no control over the crossdomain policy file, you might need to use a server side script to copy/proxy the file over to a domain that can grant your loaded swf access.
Here's a simplified version of your snippet that works (sans the external interface/pixel values part):
var myLoader:MovieClipLoader = new MovieClipLoader();
var mclListener:Object = new Object();
mclListener.onLoadInit = function(target_mc:MovieClip)
{
var pixels:Array = new Array();
setTimeout(takeSnapshot,2000,target_mc);
}
myLoader.addListener(mclListener);
myLoader.loadClip("http://www.bbc.co.uk/science/humanbody/sleep/sheep/reaction_version5.swf",1);
//myLoader.loadClip("http://i.cdn.turner.com/cnn/cnnintl_adspaces/2.0/creatives/2010/6/9/21017300x250-03.swf", 1);
//myLoader.loadClip("https://s.ytimg.com/yt/swfbin/watch_as3-vflJjAza6.swf", 0);
function takeSnapshot(target:MovieClip):Void {
var flash2canvasScreenshot:BitmapData = new BitmapData(150, 150, false, 0x00000000);//tiny sample
flash2canvasScreenshot.draw(target);
_level1._alpha = 20;//fade the loaded content
_level0.attachBitmap(flash2canvasScreenshot,0);//show the snapshop. sorry about using _root
}
Here's a quick zoomed preview of the 150x150 snap:
Here's an as3 snippet to illustrate the security sandbox handling issue:
var swf:Loader = new Loader();
swf.contentLoaderInfo.addEventListener(Event.COMPLETE,loaderComplete);
swf.contentLoaderInfo.addEventListener(SecurityErrorEvent.SECURITY_ERROR,loaderSecurityError);
swf.contentLoaderInfo.addEventListener(IOErrorEvent.IO_ERROR,loaderIOError);
swf.load(new URLRequest("http://i.cdn.turner.com/cnn/cnnintl_adspaces/2.0/creatives/2010/6/9/21017300x250-03.swf"),new LoaderContext(true));
function loaderComplete(event:Event):void{
setTimeout(takeSWFSnapshot,2000);
}
function loaderSecurityError(event:SecurityErrorEvent):void {
trace('caught security error',event.errorID,event.text);
}
function loaderIOError(event:IOErrorEvent):void{
trace('caught I/O error',event.errorID,event.text,'\tattempting to load\t',swf.contentLoaderInfo.url);
}
function takeSWFSnapshot():void{
var clone:BitmapData = new BitmapData(swf.content.width,swf.content.height,false,0);
try{
clone.draw(swf.content);
}catch(e:SecurityError){
trace(e.name,e.message,e.getStackTrace());
}
addChild(new Bitmap(clone));
}
HTH
My approach to this would be:
-Use AS3 for the reason lukevanin commented:
Just remember that AS3 can load an AS2 SWF, but an AS2 SWF cannot load
an AS3 SWF, so you actually achieve greater compatibility (with your
content) if you publish AS3
-Use a proxy file to fetch the swf file to get around sandbox violation issues (although if the swf loads external resources and uses relative paths it might get a bit more complex)
-Take a snapshot of the frame ( see George Profenza's solution )
-Encode the image using base64 and send that** to a JS method, and then decode to get the image.
** I'm pretty sure there are no size limitations...
I'm trying to write an onerror handler for images that replaces them with a loading image and then periodically tries to reload them. The problem I'm having is that if the loading image fails to load, it goes into an infinite loop of failure. I'm trying to deal with this by checking if the URL is the loading image:
if(photo.src != loadingImage) {
// Try to reload the image
}
Unfortunately, loadingImage can be a relative URL (/images/loadingImage.jpg), but photo.src is always a full URL (http://example.com/images/loadingImage.jpg). Is there any way to generate this full URL without passing the function any more information? Obviously I could pass it the host name, or require full URLs, but I'd like to keep this function's interface as simple as possible.
EDIT:
Basically what I want is to guarantee that if I do photo.src = loadingImage, that this will be true: photo.src === loadingImage. The constraint is that I know nothing about loadingImage except that it's a valid URL (it could be absolute, relative to the server, or relative to the current page). photo.src can be any (absolute) URL, so not necessarily on the same domain as loadingImage.
Here's a couple methods people have used to convert relative URLs to absolute ones in javascript:
StackOverflow - Getting an absolute URL from a relative one
Debuggable.com - Relative URLs in Javascript
Alternatively, have you considered doing the opposite - converting the absolute URL to a relative one? If loadingimage always contains the entire path section of the URL, then something like this would probably work:
var relativePhotoSrc = photo.src;
if (relativePhotoSrc.indexOf("/") > 0 && relativePhotoSrc.indexOf("http://") == 0) {
relativePhotoSrc = relativePhotoSrc.replace("http://", "");
relativePhotoSrc = relativePhotoSrc.substring(relativePhotoSrc.indexOf("/"), relativePhotoSrc.length);
}
alert(relativePhotoSrc);
if (relativePhotoSrc != loadingImage && photo.src != loadingImage) {
// Try to reload the image
}
There's probably a slightly more efficient/reliable way to do the string manipulation with a regular expression, but this seems to get the job done in my tests.
How about this? The photo should either be a full URL or relative to the current document.
var url;
// There probably other conditions to add here to make this bullet proof
if (photo.src.indexOf("http://") == 0 ||
photo.src.indexOf("https://") == 0 ||
photo.src.indexOf("//") == 0) {
url = photo.src;
} else {
url = location.href.substring(0, location.href.lastIndexOf('/')) + "/" + photo.src;
}
Just check if they end with the same string:
var u1 = '/images/loadingImage.jpg';
var u2 = 'http://example.com/images/loadingImage.jpg'
var len = Math.min(u1.length, u2.length);
var eq = u1.substring(u1.length - len) == u2.substring(u2.length - len);